riched20/tests: Use BOOL type where appropriate.
[wine.git] / dlls / riched20 / tests / editor.c
blob2fc228bfa747c0159b579c834567c2821fd6fbda
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 WCHAR *atowstr(const char *str)
222 WCHAR *ret;
223 DWORD len;
224 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
225 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
226 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
227 return ret;
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
232 int findloc;
234 if(unicode){
235 FINDTEXTW ftw;
236 memset(&ftw, 0, sizeof(ftw));
237 ftw.chrg.cpMin = f->start;
238 ftw.chrg.cpMax = f->end;
239 ftw.lpstrText = atowstr(f->needle);
241 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ftw);
242 ok(findloc == f->expected_loc,
243 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
244 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
246 findloc = SendMessage(hwnd, EM_FINDTEXTW, f->flags, (LPARAM) &ftw);
247 ok(findloc == f->expected_loc,
248 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
249 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
251 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
252 }else{
253 FINDTEXTA fta;
254 memset(&fta, 0, sizeof(fta));
255 fta.chrg.cpMin = f->start;
256 fta.chrg.cpMax = f->end;
257 fta.lpstrText = f->needle;
259 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &fta);
260 ok(findloc == f->expected_loc,
261 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
262 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
266 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
267 int id, BOOL unicode)
269 int findloc;
270 int expected_end_loc;
272 if(unicode){
273 FINDTEXTEXW ftw;
274 memset(&ftw, 0, sizeof(ftw));
275 ftw.chrg.cpMin = f->start;
276 ftw.chrg.cpMax = f->end;
277 ftw.lpstrText = atowstr(f->needle);
278 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ftw);
279 ok(findloc == f->expected_loc,
280 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
281 name, id, f->needle, f->start, f->end, f->flags, findloc);
282 ok(ftw.chrgText.cpMin == f->expected_loc,
283 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
284 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
285 expected_end_loc = ((f->expected_loc == -1) ? -1
286 : f->expected_loc + strlen(f->needle));
287 ok(ftw.chrgText.cpMax == expected_end_loc,
288 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
289 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
290 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
291 }else{
292 FINDTEXTEXA fta;
293 memset(&fta, 0, sizeof(fta));
294 fta.chrg.cpMin = f->start;
295 fta.chrg.cpMax = f->end;
296 fta.lpstrText = f->needle;
297 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &fta);
298 ok(findloc == f->expected_loc,
299 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
300 name, id, f->needle, f->start, f->end, f->flags, findloc);
301 ok(fta.chrgText.cpMin == f->expected_loc,
302 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
303 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
304 expected_end_loc = ((f->expected_loc == -1) ? -1
305 : f->expected_loc + strlen(f->needle));
306 ok(fta.chrgText.cpMax == expected_end_loc,
307 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
308 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
312 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
313 int num_tests, BOOL unicode)
315 int i;
317 for (i = 0; i < num_tests; i++) {
318 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
319 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
323 static void test_EM_FINDTEXT(BOOL unicode)
325 HWND hwndRichEdit;
326 CHARFORMAT2 cf2;
328 if(unicode)
329 hwndRichEdit = new_richeditW(NULL);
330 else
331 hwndRichEdit = new_richedit(NULL);
333 /* Empty rich edit control */
334 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
335 sizeof(find_tests)/sizeof(struct find_s), unicode);
337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
339 /* Haystack text */
340 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
341 sizeof(find_tests2)/sizeof(struct find_s), unicode);
343 /* Setting a format on an arbitrary range should have no effect in search
344 results. This tests correct offset reporting across runs. */
345 cf2.cbSize = sizeof(CHARFORMAT2);
346 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
347 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
348 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
349 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
350 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
352 /* Haystack text, again */
353 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
354 sizeof(find_tests2)/sizeof(struct find_s), unicode);
356 /* Yet another range */
357 cf2.dwMask = CFM_BOLD | cf2.dwMask;
358 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
359 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
360 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
362 /* Haystack text, again */
363 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
364 sizeof(find_tests2)/sizeof(struct find_s), unicode);
366 DestroyWindow(hwndRichEdit);
369 static const struct getline_s {
370 int line;
371 size_t buffer_len;
372 const char *text;
373 } gl[] = {
374 {0, 10, "foo bar\r"},
375 {1, 10, "\r"},
376 {2, 10, "bar\r"},
377 {3, 10, "\r"},
379 /* Buffer smaller than line length */
380 {0, 2, "foo bar\r"},
381 {0, 1, "foo bar\r"},
382 {0, 0, "foo bar\r"}
385 static void test_EM_GETLINE(void)
387 int i;
388 HWND hwndRichEdit = new_richedit(NULL);
389 static const int nBuf = 1024;
390 char dest[1024], origdest[1024];
391 const char text[] = "foo bar\n"
392 "\n"
393 "bar\n";
395 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
397 memset(origdest, 0xBB, nBuf);
398 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
400 int nCopied;
401 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
402 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
403 memset(dest, 0xBB, nBuf);
404 *(WORD *) dest = gl[i].buffer_len;
406 /* EM_GETLINE appends a "\r\0" to the end of the line
407 * nCopied counts up to and including the '\r' */
408 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
409 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
410 expected_nCopied);
411 /* two special cases since a parameter is passed via dest */
412 if (gl[i].buffer_len == 0)
413 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
414 "buffer_len=0\n");
415 else if (gl[i].buffer_len == 1)
416 ok(dest[0] == gl[i].text[0] && !dest[1] &&
417 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
418 else
420 /* Prepare hex strings of buffers to dump on failure. */
421 char expectedbuf[1024];
422 char resultbuf[1024];
423 int j;
424 resultbuf[0] = '\0';
425 for (j = 0; j < 32; j++)
426 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
427 expectedbuf[0] = '\0';
428 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
429 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
430 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
431 sprintf(expectedbuf+strlen(expectedbuf), "??");
432 for (; j < 32; j++) /* Bytes after declared buffer size */
433 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
435 /* Test the part of the buffer that is expected to be written according
436 * to the MSDN documentation fo EM_GETLINE, which does not state that
437 * a NULL terminating character will be added unless no text is copied.
439 * Windows NT does not append a NULL terminating character, but
440 * Windows 2000 and up do append a NULL terminating character if there
441 * is space in the buffer. The test will ignore this difference. */
442 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
443 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
444 i, expected_bytes_written, expectedbuf, resultbuf);
445 /* Test the part of the buffer after the declared length to make sure
446 * there are no buffer overruns. */
447 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
448 nBuf - gl[i].buffer_len),
449 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
450 i, expected_bytes_written, expectedbuf, resultbuf);
454 DestroyWindow(hwndRichEdit);
457 static void test_EM_LINELENGTH(void)
459 HWND hwndRichEdit = new_richedit(NULL);
460 const char * text =
461 "richedit1\r"
462 "richedit1\n"
463 "richedit1\r\n"
464 "richedit1";
465 int offset_test[10][2] = {
466 {0, 9},
467 {5, 9},
468 {10, 9},
469 {15, 9},
470 {20, 9},
471 {25, 9},
472 {30, 9},
473 {35, 9},
474 {40, 0},
475 {45, 0},
477 int i;
478 LRESULT result;
480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
482 for (i = 0; i < 10; i++) {
483 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
484 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
485 offset_test[i][0], result, offset_test[i][1]);
488 DestroyWindow(hwndRichEdit);
491 static int get_scroll_pos_y(HWND hwnd)
493 POINT p = {-1, -1};
494 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
495 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
496 return p.y;
499 static void move_cursor(HWND hwnd, LONG charindex)
501 CHARRANGE cr;
502 cr.cpMax = charindex;
503 cr.cpMin = charindex;
504 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
507 static void line_scroll(HWND hwnd, int amount)
509 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
512 static void test_EM_SCROLLCARET(void)
514 int prevY, curY;
515 const char text[] = "aa\n"
516 "this is a long line of text that should be longer than the "
517 "control's width\n"
518 "cc\n"
519 "dd\n"
520 "ee\n"
521 "ff\n"
522 "gg\n"
523 "hh\n";
524 /* The richedit window height needs to be large enough vertically to fit in
525 * more than two lines of text, so the new_richedit function can't be used
526 * since a height of 60 was not large enough on some systems.
528 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
529 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
530 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
531 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
533 /* Can't verify this */
534 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
538 /* Caret above visible window */
539 line_scroll(hwndRichEdit, 3);
540 prevY = get_scroll_pos_y(hwndRichEdit);
541 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
542 curY = get_scroll_pos_y(hwndRichEdit);
543 ok(prevY != curY, "%d == %d\n", prevY, curY);
545 /* Caret below visible window */
546 move_cursor(hwndRichEdit, sizeof(text) - 1);
547 line_scroll(hwndRichEdit, -3);
548 prevY = get_scroll_pos_y(hwndRichEdit);
549 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
550 curY = get_scroll_pos_y(hwndRichEdit);
551 ok(prevY != curY, "%d == %d\n", prevY, curY);
553 /* Caret in visible window */
554 move_cursor(hwndRichEdit, sizeof(text) - 2);
555 prevY = get_scroll_pos_y(hwndRichEdit);
556 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
557 curY = get_scroll_pos_y(hwndRichEdit);
558 ok(prevY == curY, "%d != %d\n", prevY, curY);
560 /* Caret still in visible window */
561 line_scroll(hwndRichEdit, -1);
562 prevY = get_scroll_pos_y(hwndRichEdit);
563 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
564 curY = get_scroll_pos_y(hwndRichEdit);
565 ok(prevY == curY, "%d != %d\n", prevY, curY);
567 DestroyWindow(hwndRichEdit);
570 static void test_EM_POSFROMCHAR(void)
572 HWND hwndRichEdit = new_richedit(NULL);
573 int i, expected;
574 LRESULT result;
575 unsigned int height = 0;
576 int xpos = 0;
577 POINTL pt;
578 LOCALESIGNATURE sig;
579 BOOL rtl;
580 static const char text[] = "aa\n"
581 "this is a long line of text that should be longer than the "
582 "control's width\n"
583 "cc\n"
584 "dd\n"
585 "ee\n"
586 "ff\n"
587 "gg\n"
588 "hh\n";
590 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
591 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
592 (sig.lsUsb[3] & 0x08000000) != 0);
594 /* Fill the control to lines to ensure that most of them are offscreen */
595 for (i = 0; i < 50; i++)
597 /* Do not modify the string; it is exactly 16 characters long. */
598 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
599 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
603 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
604 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
605 Richedit 3.0 accepts either of the above API conventions.
608 /* Testing Richedit 2.0 API format */
610 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
611 Since all lines are identical and drawn with the same font,
612 they should have the same height... right?
614 for (i = 0; i < 50; i++)
616 /* All the lines are 16 characters long */
617 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
618 if (i == 0)
620 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
621 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
622 xpos = LOWORD(result);
624 else if (i == 1)
626 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
627 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
628 height = HIWORD(result);
630 else
632 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
633 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
637 /* Testing position at end of text */
638 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
639 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
640 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
642 /* Testing position way past end of text */
643 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
644 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
645 expected = (rtl ? 8 : 1);
646 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
648 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
649 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
650 for (i = 0; i < 50; i++)
652 /* All the lines are 16 characters long */
653 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
654 ok((signed short)(HIWORD(result)) == (i - 1) * height,
655 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
656 (signed short)(HIWORD(result)), (i - 1) * height);
657 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
660 /* Testing position at end of text */
661 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
662 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
663 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
665 /* Testing position way past end of text */
666 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
667 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
668 expected = (rtl ? 8 : 1);
669 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
671 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
672 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
673 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
675 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
676 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
677 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
678 xpos = LOWORD(result);
680 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
681 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
682 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
683 ok((signed short)(LOWORD(result)) < xpos,
684 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
685 (signed short)(LOWORD(result)), xpos);
686 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
688 /* Test around end of text that doesn't end in a newline. */
689 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
690 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
691 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
692 ok(pt.x > 1, "pt.x = %d\n", pt.x);
693 xpos = pt.x;
694 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
695 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
696 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
697 xpos = (rtl ? pt.x + 7 : pt.x);
698 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
699 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
700 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
702 /* Try a negative position. */
703 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
704 ok(pt.x == 1, "pt.x = %d\n", pt.x);
706 DestroyWindow(hwndRichEdit);
709 static void test_EM_SETCHARFORMAT(void)
711 HWND hwndRichEdit = new_richedit(NULL);
712 CHARFORMAT2 cf2;
713 int rc = 0;
714 int tested_effects[] = {
715 CFE_BOLD,
716 CFE_ITALIC,
717 CFE_UNDERLINE,
718 CFE_STRIKEOUT,
719 CFE_PROTECTED,
720 CFE_LINK,
721 CFE_SUBSCRIPT,
722 CFE_SUPERSCRIPT,
725 int i;
726 CHARRANGE cr;
727 LOCALESIGNATURE sig;
728 BOOL rtl;
730 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
731 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
732 (sig.lsUsb[3] & 0x08000000) != 0);
734 /* Invalid flags, CHARFORMAT2 structure blanked out */
735 memset(&cf2, 0, sizeof(cf2));
736 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
737 (LPARAM) &cf2);
738 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
740 /* A valid flag, CHARFORMAT2 structure blanked out */
741 memset(&cf2, 0, sizeof(cf2));
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
743 (LPARAM) &cf2);
744 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
746 /* A valid flag, CHARFORMAT2 structure blanked out */
747 memset(&cf2, 0, sizeof(cf2));
748 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
749 (LPARAM) &cf2);
750 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
752 /* A valid flag, CHARFORMAT2 structure blanked out */
753 memset(&cf2, 0, sizeof(cf2));
754 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
755 (LPARAM) &cf2);
756 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
758 /* A valid flag, CHARFORMAT2 structure blanked out */
759 memset(&cf2, 0, sizeof(cf2));
760 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
761 (LPARAM) &cf2);
762 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
764 /* Invalid flags, CHARFORMAT2 structure minimally filled */
765 memset(&cf2, 0, sizeof(cf2));
766 cf2.cbSize = sizeof(CHARFORMAT2);
767 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
768 (LPARAM) &cf2);
769 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
770 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
771 ok(rc == FALSE, "Should not be able to undo here.\n");
772 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
774 /* A valid flag, CHARFORMAT2 structure minimally filled */
775 memset(&cf2, 0, sizeof(cf2));
776 cf2.cbSize = sizeof(CHARFORMAT2);
777 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
778 (LPARAM) &cf2);
779 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
780 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
781 ok(rc == FALSE, "Should not be able to undo here.\n");
782 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
784 /* A valid flag, CHARFORMAT2 structure minimally filled */
785 memset(&cf2, 0, sizeof(cf2));
786 cf2.cbSize = sizeof(CHARFORMAT2);
787 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
788 (LPARAM) &cf2);
789 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
790 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
791 ok(rc == FALSE, "Should not be able to undo here.\n");
792 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
794 /* A valid flag, CHARFORMAT2 structure minimally filled */
795 memset(&cf2, 0, sizeof(cf2));
796 cf2.cbSize = sizeof(CHARFORMAT2);
797 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
798 (LPARAM) &cf2);
799 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
800 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
801 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
802 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
804 /* A valid flag, CHARFORMAT2 structure minimally filled */
805 memset(&cf2, 0, sizeof(cf2));
806 cf2.cbSize = sizeof(CHARFORMAT2);
807 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
808 (LPARAM) &cf2);
809 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
810 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
811 ok(rc == TRUE, "Should not be able to undo here.\n");
812 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
814 cf2.cbSize = sizeof(CHARFORMAT2);
815 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
816 (LPARAM) &cf2);
818 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
821 (LPARAM) &cf2);
822 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
823 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
825 /* wParam==0 is default char format, does not set modify */
826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
827 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
828 ok(rc == 0, "Text marked as modified, expected not modified!\n");
829 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
830 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
831 if (! rtl)
833 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
834 ok(rc == 0, "Text marked as modified, expected not modified!\n");
836 else
837 skip("RTL language found\n");
839 /* wParam==SCF_SELECTION sets modify if nonempty selection */
840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
841 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
842 ok(rc == 0, "Text marked as modified, expected not modified!\n");
843 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
844 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
845 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
846 ok(rc == 0, "Text marked as modified, expected not modified!\n");
848 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
849 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
850 ok(rc == 0, "Text marked as modified, expected not modified!\n");
851 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
853 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
854 ok(rc == 0, "Text marked as modified, expected not modified!\n");
855 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
856 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
858 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
859 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
861 /* wParam==SCF_ALL sets modify regardless of whether text is present */
862 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
863 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
864 ok(rc == 0, "Text marked as modified, expected not modified!\n");
865 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
866 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
867 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
868 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
870 DestroyWindow(hwndRichEdit);
872 /* EM_GETCHARFORMAT tests */
873 for (i = 0; tested_effects[i]; i++)
875 hwndRichEdit = new_richedit(NULL);
876 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
878 /* Need to set a TrueType font to get consistent CFM_BOLD results */
879 memset(&cf2, 0, sizeof(CHARFORMAT2));
880 cf2.cbSize = sizeof(CHARFORMAT2);
881 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
882 cf2.dwEffects = 0;
883 strcpy(cf2.szFaceName, "Courier New");
884 cf2.wWeight = FW_DONTCARE;
885 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
887 memset(&cf2, 0, sizeof(CHARFORMAT2));
888 cf2.cbSize = sizeof(CHARFORMAT2);
889 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
890 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
891 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
892 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
894 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
895 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
896 ok((cf2.dwEffects & tested_effects[i]) == 0,
897 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
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, 0, 2);
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]) == tested_effects[i],
918 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\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]) == 0,
930 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\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]);
942 DestroyWindow(hwndRichEdit);
945 for (i = 0; tested_effects[i]; i++)
947 hwndRichEdit = new_richedit(NULL);
948 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
950 /* Need to set a TrueType font to get consistent CFM_BOLD results */
951 memset(&cf2, 0, sizeof(CHARFORMAT2));
952 cf2.cbSize = sizeof(CHARFORMAT2);
953 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
954 cf2.dwEffects = 0;
955 strcpy(cf2.szFaceName, "Courier New");
956 cf2.wWeight = FW_DONTCARE;
957 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
959 memset(&cf2, 0, sizeof(CHARFORMAT2));
960 cf2.cbSize = sizeof(CHARFORMAT2);
961 cf2.dwMask = tested_effects[i];
962 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
963 cf2.dwMask = CFM_SUPERSCRIPT;
964 cf2.dwEffects = tested_effects[i];
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
966 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
968 memset(&cf2, 0, sizeof(CHARFORMAT2));
969 cf2.cbSize = sizeof(CHARFORMAT2);
970 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
971 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
973 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
976 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
977 ok((cf2.dwEffects & tested_effects[i]) == 0,
978 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980 memset(&cf2, 0, sizeof(CHARFORMAT2));
981 cf2.cbSize = sizeof(CHARFORMAT2);
982 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
983 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
987 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
988 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
989 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
990 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
992 memset(&cf2, 0, sizeof(CHARFORMAT2));
993 cf2.cbSize = sizeof(CHARFORMAT2);
994 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
995 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
996 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
997 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
999 (cf2.dwMask & tested_effects[i]) == 0),
1000 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1001 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1002 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1004 DestroyWindow(hwndRichEdit);
1007 /* Effects applied on an empty selection should take effect when selection is
1008 replaced with text */
1009 hwndRichEdit = new_richedit(NULL);
1010 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1011 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1013 memset(&cf2, 0, sizeof(CHARFORMAT2));
1014 cf2.cbSize = sizeof(CHARFORMAT2);
1015 cf2.dwMask = CFM_BOLD;
1016 cf2.dwEffects = CFE_BOLD;
1017 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1019 /* Selection is now nonempty */
1020 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1022 memset(&cf2, 0, sizeof(CHARFORMAT2));
1023 cf2.cbSize = sizeof(CHARFORMAT2);
1024 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1025 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1027 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1028 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1029 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1030 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1033 /* Set two effects on an empty selection */
1034 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1035 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1037 memset(&cf2, 0, sizeof(CHARFORMAT2));
1038 cf2.cbSize = sizeof(CHARFORMAT2);
1039 cf2.dwMask = CFM_BOLD;
1040 cf2.dwEffects = CFE_BOLD;
1041 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042 cf2.dwMask = CFM_ITALIC;
1043 cf2.dwEffects = CFE_ITALIC;
1044 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1046 /* Selection is now nonempty */
1047 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1049 memset(&cf2, 0, sizeof(CHARFORMAT2));
1050 cf2.cbSize = sizeof(CHARFORMAT2);
1051 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1052 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1054 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1055 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1056 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1057 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1059 /* Setting the (empty) selection to exactly the same place as before should
1060 NOT clear the insertion style! */
1061 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1062 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1064 memset(&cf2, 0, sizeof(CHARFORMAT2));
1065 cf2.cbSize = sizeof(CHARFORMAT2);
1066 cf2.dwMask = CFM_BOLD;
1067 cf2.dwEffects = CFE_BOLD;
1068 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1070 /* Empty selection in same place, insert style should NOT be forgotten here. */
1071 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1073 /* Selection is now nonempty */
1074 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1076 memset(&cf2, 0, sizeof(CHARFORMAT2));
1077 cf2.cbSize = sizeof(CHARFORMAT2);
1078 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1081 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1082 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1083 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1084 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1086 /* Ditto with EM_EXSETSEL */
1087 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1088 cr.cpMin = 2; cr.cpMax = 2;
1089 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1091 memset(&cf2, 0, sizeof(CHARFORMAT2));
1092 cf2.cbSize = sizeof(CHARFORMAT2);
1093 cf2.dwMask = CFM_BOLD;
1094 cf2.dwEffects = CFE_BOLD;
1095 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1097 /* Empty selection in same place, insert style should NOT be forgotten here. */
1098 cr.cpMin = 2; cr.cpMax = 2;
1099 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1101 /* Selection is now nonempty */
1102 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1104 memset(&cf2, 0, sizeof(CHARFORMAT2));
1105 cf2.cbSize = sizeof(CHARFORMAT2);
1106 cr.cpMin = 2; cr.cpMax = 6;
1107 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1108 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1110 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1111 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1112 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1113 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1115 DestroyWindow(hwndRichEdit);
1118 static void test_EM_SETTEXTMODE(void)
1120 HWND hwndRichEdit = new_richedit(NULL);
1121 CHARFORMAT2 cf2, cf2test;
1122 CHARRANGE cr;
1123 int rc = 0;
1125 /*Attempt to use mutually exclusive modes*/
1126 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1127 ok(rc == E_INVALIDARG,
1128 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1130 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1131 /*Insert text into the control*/
1133 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1135 /*Attempt to change the control to plain text mode*/
1136 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1137 ok(rc == E_UNEXPECTED,
1138 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1140 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1141 If rich text is pasted, it should have the same formatting as the rest
1142 of the text in the control*/
1144 /*Italicize the text
1145 *NOTE: If the default text was already italicized, the test will simply
1146 reverse; in other words, it will copy a regular "wine" into a plain
1147 text window that uses an italicized format*/
1148 cf2.cbSize = sizeof(CHARFORMAT2);
1149 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1150 (LPARAM) &cf2);
1152 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1153 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1155 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1156 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1158 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1159 however, SCF_ALL has been implemented*/
1160 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1161 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1163 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1164 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1168 /*Select the string "wine"*/
1169 cr.cpMin = 0;
1170 cr.cpMax = 4;
1171 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1173 /*Copy the italicized "wine" to the clipboard*/
1174 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1176 /*Reset the formatting to default*/
1177 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1178 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1179 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1181 /*Clear the text in the control*/
1182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1184 /*Switch to Plain Text Mode*/
1185 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1186 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1188 /*Input "wine" again in normal format*/
1189 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1191 /*Paste the italicized "wine" into the control*/
1192 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1194 /*Select a character from the first "wine" string*/
1195 cr.cpMin = 2;
1196 cr.cpMax = 3;
1197 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1199 /*Retrieve its formatting*/
1200 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1201 (LPARAM) &cf2);
1203 /*Select a character from the second "wine" string*/
1204 cr.cpMin = 5;
1205 cr.cpMax = 6;
1206 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1208 /*Retrieve its formatting*/
1209 cf2test.cbSize = sizeof(CHARFORMAT2);
1210 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1211 (LPARAM) &cf2test);
1213 /*Compare the two formattings*/
1214 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1215 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1216 cf2.dwEffects, cf2test.dwEffects);
1217 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1218 printing "wine" in the current format(normal)
1219 pasting "wine" from the clipboard(italicized)
1220 comparing the two formats(should differ)*/
1222 /*Attempt to switch with text in control*/
1223 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1224 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1226 /*Clear control*/
1227 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 /*Switch into Rich Text mode*/
1230 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1231 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1233 /*Print "wine" in normal formatting into the control*/
1234 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1236 /*Paste italicized "wine" into the control*/
1237 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1239 /*Select text from the first "wine" string*/
1240 cr.cpMin = 1;
1241 cr.cpMax = 3;
1242 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1244 /*Retrieve its formatting*/
1245 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1246 (LPARAM) &cf2);
1248 /*Select text from the second "wine" string*/
1249 cr.cpMin = 6;
1250 cr.cpMax = 7;
1251 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1253 /*Retrieve its formatting*/
1254 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1255 (LPARAM) &cf2test);
1257 /*Test that the two formattings are not the same*/
1258 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1259 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1260 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1262 DestroyWindow(hwndRichEdit);
1265 static void test_SETPARAFORMAT(void)
1267 HWND hwndRichEdit = new_richedit(NULL);
1268 PARAFORMAT2 fmt;
1269 HRESULT ret;
1270 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1271 fmt.cbSize = sizeof(PARAFORMAT2);
1272 fmt.dwMask = PFM_ALIGNMENT;
1273 fmt.wAlignment = PFA_LEFT;
1275 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1276 ok(ret != 0, "expected non-zero got %d\n", ret);
1278 fmt.cbSize = sizeof(PARAFORMAT2);
1279 fmt.dwMask = -1;
1280 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1281 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1282 * between richedit different native builds of riched20.dll
1283 * used on different Windows versions. */
1284 ret &= ~PFM_TABLEROWDELIMITER;
1285 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1287 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1288 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1290 DestroyWindow(hwndRichEdit);
1293 static void test_TM_PLAINTEXT(void)
1295 /*Tests plain text properties*/
1297 HWND hwndRichEdit = new_richedit(NULL);
1298 CHARFORMAT2 cf2, cf2test;
1299 CHARRANGE cr;
1300 int rc = 0;
1302 /*Switch to plain text mode*/
1304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1305 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1307 /*Fill control with text*/
1309 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1311 /*Select some text and bold it*/
1313 cr.cpMin = 10;
1314 cr.cpMax = 20;
1315 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1316 cf2.cbSize = sizeof(CHARFORMAT2);
1317 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1319 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1320 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1322 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1323 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1325 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1326 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1328 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1329 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1331 /*Get the formatting of those characters*/
1333 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1335 /*Get the formatting of some other characters*/
1336 cf2test.cbSize = sizeof(CHARFORMAT2);
1337 cr.cpMin = 21;
1338 cr.cpMax = 30;
1339 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1340 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1342 /*Test that they are the same as plain text allows only one formatting*/
1344 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1345 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1346 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1348 /*Fill the control with a "wine" string, which when inserted will be bold*/
1350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1352 /*Copy the bolded "wine" string*/
1354 cr.cpMin = 0;
1355 cr.cpMax = 4;
1356 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1357 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1359 /*Swap back to rich text*/
1361 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1362 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1364 /*Set the default formatting to bold italics*/
1366 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1367 cf2.dwMask |= CFM_ITALIC;
1368 cf2.dwEffects ^= CFE_ITALIC;
1369 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1370 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1372 /*Set the text in the control to "wine", which will be bold and italicized*/
1374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1376 /*Paste the plain text "wine" string, which should take the insert
1377 formatting, which at the moment is bold italics*/
1379 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1381 /*Select the first "wine" string and retrieve its formatting*/
1383 cr.cpMin = 1;
1384 cr.cpMax = 3;
1385 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1386 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1388 /*Select the second "wine" string and retrieve its formatting*/
1390 cr.cpMin = 5;
1391 cr.cpMax = 7;
1392 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1393 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1395 /*Compare the two formattings. They should be the same.*/
1397 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1398 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1399 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1400 DestroyWindow(hwndRichEdit);
1403 static void test_WM_GETTEXT(void)
1405 HWND hwndRichEdit = new_richedit(NULL);
1406 static const char text[] = "Hello. My name is RichEdit!";
1407 static const char text2[] = "Hello. My name is RichEdit!\r";
1408 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1409 char buffer[1024] = {0};
1410 int result;
1412 /* Baseline test with normal-sized buffer */
1413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1414 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1415 ok(result == lstrlen(buffer),
1416 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1417 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1418 result = strcmp(buffer,text);
1419 ok(result == 0,
1420 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1422 /* Test for returned value of WM_GETTEXTLENGTH */
1423 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1424 ok(result == lstrlen(text),
1425 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1426 result, lstrlen(text));
1428 /* Test for behavior in overflow case */
1429 memset(buffer, 0, 1024);
1430 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1431 ok(result == 0 ||
1432 result == lstrlenA(text) - 1, /* XP, win2k3 */
1433 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1434 result = strcmp(buffer,text);
1435 if (result)
1436 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1437 ok(result == 0,
1438 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1440 /* Baseline test with normal-sized buffer and carriage return */
1441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1442 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1443 ok(result == lstrlen(buffer),
1444 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1445 result = strcmp(buffer,text2_after);
1446 ok(result == 0,
1447 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1449 /* Test for returned value of WM_GETTEXTLENGTH */
1450 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1451 ok(result == lstrlen(text2_after),
1452 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1453 result, lstrlen(text2_after));
1455 /* Test for behavior of CRLF conversion in case of overflow */
1456 memset(buffer, 0, 1024);
1457 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1458 ok(result == 0 ||
1459 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1460 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1461 result = strcmp(buffer,text2);
1462 if (result)
1463 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1464 ok(result == 0,
1465 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1467 DestroyWindow(hwndRichEdit);
1470 static void test_EM_GETTEXTRANGE(void)
1472 HWND hwndRichEdit = new_richedit(NULL);
1473 const char * text1 = "foo bar\r\nfoo bar";
1474 const char * text2 = "foo bar\rfoo bar";
1475 const char * expect = "bar\rfoo";
1476 char buffer[1024] = {0};
1477 LRESULT result;
1478 TEXTRANGEA textRange;
1480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1482 textRange.lpstrText = buffer;
1483 textRange.chrg.cpMin = 4;
1484 textRange.chrg.cpMax = 11;
1485 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1486 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1489 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1491 textRange.lpstrText = buffer;
1492 textRange.chrg.cpMin = 4;
1493 textRange.chrg.cpMax = 11;
1494 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1495 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1496 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1498 /* cpMax of text length is used instead of -1 in this case */
1499 textRange.lpstrText = buffer;
1500 textRange.chrg.cpMin = 0;
1501 textRange.chrg.cpMax = -1;
1502 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1503 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1504 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1506 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1507 textRange.lpstrText = buffer;
1508 textRange.chrg.cpMin = -1;
1509 textRange.chrg.cpMax = 1;
1510 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1511 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1512 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1514 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1515 textRange.lpstrText = buffer;
1516 textRange.chrg.cpMin = 1;
1517 textRange.chrg.cpMax = -1;
1518 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1519 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1520 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1522 /* no end character is copied if cpMax - cpMin < 0 */
1523 textRange.lpstrText = buffer;
1524 textRange.chrg.cpMin = 5;
1525 textRange.chrg.cpMax = 5;
1526 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1527 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1528 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1530 /* cpMax of text length is used if cpMax > text length*/
1531 textRange.lpstrText = buffer;
1532 textRange.chrg.cpMin = 0;
1533 textRange.chrg.cpMax = 1000;
1534 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1535 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1536 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1538 DestroyWindow(hwndRichEdit);
1541 static void test_EM_GETSELTEXT(void)
1543 HWND hwndRichEdit = new_richedit(NULL);
1544 const char * text1 = "foo bar\r\nfoo bar";
1545 const char * text2 = "foo bar\rfoo bar";
1546 const char * expect = "bar\rfoo";
1547 char buffer[1024] = {0};
1548 LRESULT result;
1550 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1552 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1553 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1554 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1555 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1559 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1560 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1561 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1562 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1564 DestroyWindow(hwndRichEdit);
1567 /* FIXME: need to test unimplemented options and robustly test wparam */
1568 static void test_EM_SETOPTIONS(void)
1570 HWND hwndRichEdit;
1571 static const char text[] = "Hello. My name is RichEdit!";
1572 char buffer[1024] = {0};
1573 DWORD dwStyle, options, oldOptions;
1574 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1575 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1576 ES_SELECTIONBAR|ES_VERTICAL;
1578 /* Test initial options. */
1579 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1580 0, 0, 200, 60, NULL, NULL,
1581 hmoduleRichEdit, NULL);
1582 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1583 RICHEDIT_CLASS, (int) GetLastError());
1584 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1585 ok(options == 0, "Incorrect initial options %x\n", options);
1586 DestroyWindow(hwndRichEdit);
1588 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1589 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1590 0, 0, 200, 60, NULL, NULL,
1591 hmoduleRichEdit, NULL);
1592 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1593 RICHEDIT_CLASS, (int) GetLastError());
1594 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1595 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1596 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1597 "Incorrect initial options %x\n", options);
1599 /* NEGATIVE TESTING - NO OPTIONS SET */
1600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1601 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1603 /* testing no readonly by sending 'a' to the control*/
1604 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1605 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1606 ok(buffer[0]=='a',
1607 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1608 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1610 /* READONLY - sending 'a' to the control */
1611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1612 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1613 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1614 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1615 ok(buffer[0]==text[0],
1616 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1618 /* EM_SETOPTIONS changes the window style, but changing the
1619 * window style does not change the options. */
1620 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1621 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1622 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1623 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1624 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1625 /* Confirm that the text is still read only. */
1626 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1627 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1628 ok(buffer[0]==text[0],
1629 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1631 oldOptions = options;
1632 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1633 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1634 ok(options == oldOptions,
1635 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1637 DestroyWindow(hwndRichEdit);
1640 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1642 CHARFORMAT2W text_format;
1643 text_format.cbSize = sizeof(text_format);
1644 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1645 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1646 return (text_format.dwEffects & CFE_LINK) != 0;
1649 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1651 BOOL link_present = FALSE;
1653 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1654 if (is_url)
1655 { /* control text is url; should get CFE_LINK */
1656 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1658 else
1660 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1664 static HWND new_static_wnd(HWND parent) {
1665 return new_window("Static", 0, parent);
1668 static void test_EM_AUTOURLDETECT(void)
1670 /* DO NOT change the properties of the first two elements. To shorten the
1671 tests, all tests after WM_SETTEXT test just the first two elements -
1672 one non-URL and one URL */
1673 struct urls_s {
1674 const char *text;
1675 BOOL is_url;
1676 } urls[12] = {
1677 {"winehq.org", FALSE},
1678 {"http://www.winehq.org", TRUE},
1679 {"http//winehq.org", FALSE},
1680 {"ww.winehq.org", FALSE},
1681 {"www.winehq.org", TRUE},
1682 {"ftp://192.168.1.1", TRUE},
1683 {"ftp//192.168.1.1", FALSE},
1684 {"mailto:your@email.com", TRUE},
1685 {"prospero:prosperoserver", TRUE},
1686 {"telnet:test", TRUE},
1687 {"news:newserver", TRUE},
1688 {"wais:waisserver", TRUE}
1691 int i, j;
1692 int urlRet=-1;
1693 HWND hwndRichEdit, parent;
1695 /* All of the following should cause the URL to be detected */
1696 const char * templates_delim[] = {
1697 "This is some text with X on it",
1698 "This is some text with (X) on it",
1699 "This is some text with X\r on it",
1700 "This is some text with ---X--- on it",
1701 "This is some text with \"X\" on it",
1702 "This is some text with 'X' on it",
1703 "This is some text with 'X' on it",
1704 "This is some text with :X: on it",
1706 "This text ends with X",
1708 "This is some text with X) on it",
1709 "This is some text with X--- on it",
1710 "This is some text with X\" on it",
1711 "This is some text with X' on it",
1712 "This is some text with X: on it",
1714 "This is some text with (X on it",
1715 "This is some text with \rX on it",
1716 "This is some text with ---X on it",
1717 "This is some text with \"X on it",
1718 "This is some text with 'X on it",
1719 "This is some text with :X on it",
1721 /* None of these should cause the URL to be detected */
1722 const char * templates_non_delim[] = {
1723 "This is some text with |X| on it",
1724 "This is some text with *X* on it",
1725 "This is some text with /X/ on it",
1726 "This is some text with +X+ on it",
1727 "This is some text with %X% on it",
1728 "This is some text with #X# on it",
1729 "This is some text with @X@ on it",
1730 "This is some text with \\X\\ on it",
1731 "This is some text with |X on it",
1732 "This is some text with *X on it",
1733 "This is some text with /X on it",
1734 "This is some text with +X on it",
1735 "This is some text with %X on it",
1736 "This is some text with #X on it",
1737 "This is some text with @X on it",
1738 "This is some text with \\X on it",
1740 /* All of these cause the URL detection to be extended by one more byte,
1741 thus demonstrating that the tested character is considered as part
1742 of the URL. */
1743 const char * templates_xten_delim[] = {
1744 "This is some text with X| on it",
1745 "This is some text with X* on it",
1746 "This is some text with X/ on it",
1747 "This is some text with X+ on it",
1748 "This is some text with X% on it",
1749 "This is some text with X# on it",
1750 "This is some text with X@ on it",
1751 "This is some text with X\\ on it",
1753 char buffer[1024];
1755 parent = new_static_wnd(NULL);
1756 hwndRichEdit = new_richedit(parent);
1757 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1758 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1759 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1760 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1761 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1762 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1763 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1764 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1765 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1766 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1767 /* for each url, check the text to see if CFE_LINK effect is present */
1768 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1770 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1772 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1774 /* Link detection should happen immediately upon WM_SETTEXT */
1775 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1777 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1779 DestroyWindow(hwndRichEdit);
1781 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1782 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1783 hwndRichEdit = new_richedit(parent);
1785 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1786 char * at_pos;
1787 int at_offset;
1788 int end_offset;
1790 at_pos = strchr(templates_delim[j], 'X');
1791 at_offset = at_pos - templates_delim[j];
1792 strncpy(buffer, templates_delim[j], at_offset);
1793 buffer[at_offset] = '\0';
1794 strcat(buffer, urls[i].text);
1795 strcat(buffer, templates_delim[j] + at_offset + 1);
1796 end_offset = at_offset + strlen(urls[i].text);
1798 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1801 /* This assumes no templates start with the URL itself, and that they
1802 have at least two characters before the URL text */
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1805 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1806 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1807 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1808 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1810 if (urls[i].is_url)
1812 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1813 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1814 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1815 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1817 else
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1824 if (buffer[end_offset] != '\0')
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1828 if (buffer[end_offset +1] != '\0')
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1836 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1837 char * at_pos;
1838 int at_offset;
1839 int end_offset;
1841 at_pos = strchr(templates_non_delim[j], 'X');
1842 at_offset = at_pos - templates_non_delim[j];
1843 strncpy(buffer, templates_non_delim[j], at_offset);
1844 buffer[at_offset] = '\0';
1845 strcat(buffer, urls[i].text);
1846 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1847 end_offset = at_offset + strlen(urls[i].text);
1849 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1852 /* This assumes no templates start with the URL itself, and that they
1853 have at least two characters before the URL text */
1854 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1855 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1861 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1862 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1865 if (buffer[end_offset] != '\0')
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1869 if (buffer[end_offset +1] != '\0')
1871 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1872 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1877 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1878 char * at_pos;
1879 int at_offset;
1880 int end_offset;
1882 at_pos = strchr(templates_xten_delim[j], 'X');
1883 at_offset = at_pos - templates_xten_delim[j];
1884 strncpy(buffer, templates_xten_delim[j], at_offset);
1885 buffer[at_offset] = '\0';
1886 strcat(buffer, urls[i].text);
1887 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1888 end_offset = at_offset + strlen(urls[i].text);
1890 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1893 /* This assumes no templates start with the URL itself, and that they
1894 have at least two characters before the URL text */
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1902 if (urls[i].is_url)
1904 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1905 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1906 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1907 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1908 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1909 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1911 else
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1915 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1916 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1917 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1918 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1920 if (buffer[end_offset +1] != '\0')
1922 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1923 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1924 if (buffer[end_offset +2] != '\0')
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1932 DestroyWindow(hwndRichEdit);
1933 hwndRichEdit = NULL;
1936 /* Test detection of URLs within normal text - WM_CHAR case. */
1937 /* Test only the first two URL examples for brevity */
1938 for (i = 0; i < 2; i++) {
1939 hwndRichEdit = new_richedit(parent);
1941 /* Also for brevity, test only the first three delimiters */
1942 for (j = 0; j < 3; j++) {
1943 char * at_pos;
1944 int at_offset;
1945 int end_offset;
1946 int u, v;
1948 at_pos = strchr(templates_delim[j], 'X');
1949 at_offset = at_pos - templates_delim[j];
1950 end_offset = at_offset + strlen(urls[i].text);
1952 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1953 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1954 for (u = 0; templates_delim[j][u]; u++) {
1955 if (templates_delim[j][u] == '\r') {
1956 simulate_typing_characters(hwndRichEdit, "\r");
1957 } else if (templates_delim[j][u] != 'X') {
1958 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1959 } else {
1960 for (v = 0; urls[i].text[v]; v++) {
1961 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1965 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1967 /* This assumes no templates start with the URL itself, and that they
1968 have at least two characters before the URL text */
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1976 if (urls[i].is_url)
1978 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1980 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1981 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1983 else
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1990 if (buffer[end_offset] != '\0')
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1994 if (buffer[end_offset +1] != '\0')
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2001 /* The following will insert a paragraph break after the first character
2002 of the URL candidate, thus breaking the URL. It is expected that the
2003 CFE_LINK attribute should break across both pieces of the URL */
2004 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2005 simulate_typing_characters(hwndRichEdit, "\r");
2006 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2008 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2009 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2010 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2011 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2012 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2013 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017 /* end_offset moved because of paragraph break */
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2020 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2021 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2023 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2024 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2025 if (buffer[end_offset +2] != '\0')
2027 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2028 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2032 /* The following will remove the just-inserted paragraph break, thus
2033 restoring the URL */
2034 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2035 simulate_typing_characters(hwndRichEdit, "\b");
2036 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2038 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2039 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2042 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2043 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2045 if (urls[i].is_url)
2047 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2049 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2050 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2052 else
2054 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2055 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2056 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2057 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2059 if (buffer[end_offset] != '\0')
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2063 if (buffer[end_offset +1] != '\0')
2065 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2066 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2070 DestroyWindow(hwndRichEdit);
2071 hwndRichEdit = NULL;
2074 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2075 /* Test just the first two URL examples for brevity */
2076 for (i = 0; i < 2; i++) {
2077 SETTEXTEX st;
2079 hwndRichEdit = new_richedit(parent);
2081 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2082 be detected:
2083 1) Set entire text, a la WM_SETTEXT
2084 2) Set a selection of the text to the URL
2085 3) Set a portion of the text at a time, which eventually results in
2086 an URL
2087 All of them should give equivalent results
2090 /* Set entire text in one go, like WM_SETTEXT */
2091 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2092 char * at_pos;
2093 int at_offset;
2094 int end_offset;
2096 st.codepage = CP_ACP;
2097 st.flags = ST_DEFAULT;
2099 at_pos = strchr(templates_delim[j], 'X');
2100 at_offset = at_pos - templates_delim[j];
2101 strncpy(buffer, templates_delim[j], at_offset);
2102 buffer[at_offset] = '\0';
2103 strcat(buffer, urls[i].text);
2104 strcat(buffer, templates_delim[j] + at_offset + 1);
2105 end_offset = at_offset + strlen(urls[i].text);
2107 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2108 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2110 /* This assumes no templates start with the URL itself, and that they
2111 have at least two characters before the URL text */
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2119 if (urls[i].is_url)
2121 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2126 else
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2133 if (buffer[end_offset] != '\0')
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137 if (buffer[end_offset +1] != '\0')
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2145 /* Set selection with X to the URL */
2146 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2147 char * at_pos;
2148 int at_offset;
2149 int end_offset;
2151 at_pos = strchr(templates_delim[j], 'X');
2152 at_offset = at_pos - templates_delim[j];
2153 end_offset = at_offset + strlen(urls[i].text);
2155 st.codepage = CP_ACP;
2156 st.flags = ST_DEFAULT;
2157 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2158 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2159 st.flags = ST_SELECTION;
2160 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2161 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2162 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2164 /* This assumes no templates start with the URL itself, and that they
2165 have at least two characters before the URL text */
2166 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2167 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2173 if (urls[i].is_url)
2175 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2176 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2177 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2178 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2180 else
2182 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2183 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2184 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2185 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2187 if (buffer[end_offset] != '\0')
2189 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2190 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2191 if (buffer[end_offset +1] != '\0')
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2199 /* Set selection with X to the first character of the URL, then the rest */
2200 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2201 char * at_pos;
2202 int at_offset;
2203 int end_offset;
2205 at_pos = strchr(templates_delim[j], 'X');
2206 at_offset = at_pos - templates_delim[j];
2207 end_offset = at_offset + strlen(urls[i].text);
2209 strcpy(buffer, "YY");
2210 buffer[0] = urls[i].text[0];
2212 st.codepage = CP_ACP;
2213 st.flags = ST_DEFAULT;
2214 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2215 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2216 st.flags = ST_SELECTION;
2217 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2218 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2219 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2220 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
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 DestroyWindow(hwndRichEdit);
2259 hwndRichEdit = NULL;
2262 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2263 /* Test just the first two URL examples for brevity */
2264 for (i = 0; i < 2; i++) {
2265 hwndRichEdit = new_richedit(parent);
2267 /* Set selection with X to the URL */
2268 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2269 char * at_pos;
2270 int at_offset;
2271 int end_offset;
2273 at_pos = strchr(templates_delim[j], 'X');
2274 at_offset = at_pos - templates_delim[j];
2275 end_offset = at_offset + strlen(urls[i].text);
2277 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2279 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2280 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2281 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2283 /* This assumes no templates start with the URL itself, and that they
2284 have at least two characters before the URL text */
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2287 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2288 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2289 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2290 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2292 if (urls[i].is_url)
2294 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2295 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2296 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2297 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2299 else
2301 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2302 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2303 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2304 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2306 if (buffer[end_offset] != '\0')
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2310 if (buffer[end_offset +1] != '\0')
2312 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2313 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2318 /* Set selection with X to the first character of the URL, then the rest */
2319 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2320 char * at_pos;
2321 int at_offset;
2322 int end_offset;
2324 at_pos = strchr(templates_delim[j], 'X');
2325 at_offset = at_pos - templates_delim[j];
2326 end_offset = at_offset + strlen(urls[i].text);
2328 strcpy(buffer, "YY");
2329 buffer[0] = urls[i].text[0];
2331 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2332 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2333 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2334 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2335 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2336 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2337 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2339 /* This assumes no templates start with the URL itself, and that they
2340 have at least two characters before the URL text */
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2343 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2344 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2345 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2346 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2348 if (urls[i].is_url)
2350 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2351 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2352 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2353 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2355 else
2357 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2358 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2359 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2360 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2362 if (buffer[end_offset] != '\0')
2364 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2365 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2366 if (buffer[end_offset +1] != '\0')
2368 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2369 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2374 DestroyWindow(hwndRichEdit);
2375 hwndRichEdit = NULL;
2378 DestroyWindow(parent);
2381 static void test_EM_SCROLL(void)
2383 int i, j;
2384 int r; /* return value */
2385 int expr; /* expected return value */
2386 HWND hwndRichEdit = new_richedit(NULL);
2387 int y_before, y_after; /* units of lines of text */
2389 /* test a richedit box containing a single line of text */
2390 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2391 expr = 0x00010000;
2392 for (i = 0; i < 4; i++) {
2393 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2395 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2396 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2397 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2398 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2399 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2400 "(i == %d)\n", y_after, i);
2404 * test a richedit box that will scroll. There are two general
2405 * cases: the case without any long lines and the case with a long
2406 * line.
2408 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2409 if (i == 0)
2410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2411 else
2412 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2413 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2414 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2415 "LONG LINE \nb\nc\nd\ne");
2416 for (j = 0; j < 12; j++) /* reset scroll position to top */
2417 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2419 /* get first visible line */
2420 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2423 /* get new current first visible line */
2424 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2426 ok(((r & 0xffffff00) == 0x00010000) &&
2427 ((r & 0x000000ff) != 0x00000000),
2428 "EM_SCROLL page down didn't scroll by a small positive number of "
2429 "lines (r == 0x%08x)\n", r);
2430 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2431 "(line %d scrolled to line %d\n", y_before, y_after);
2433 y_before = y_after;
2435 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2436 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2437 ok(((r & 0xffffff00) == 0x0001ff00),
2438 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2439 "(r == 0x%08x)\n", r);
2440 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2441 "%d scrolled to line %d\n", y_before, y_after);
2443 y_before = y_after;
2445 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2447 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2449 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2450 "(r == 0x%08x)\n", r);
2451 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2452 "1 line (%d scrolled to %d)\n", y_before, y_after);
2454 y_before = y_after;
2456 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2458 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2460 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2461 "(r == 0x%08x)\n", r);
2462 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2463 "line (%d scrolled to %d)\n", y_before, y_after);
2465 y_before = y_after;
2467 r = SendMessage(hwndRichEdit, EM_SCROLL,
2468 SB_LINEUP, 0); /* lineup beyond top */
2470 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2472 ok(r == 0x00010000,
2473 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2474 ok(y_before == y_after,
2475 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2477 y_before = y_after;
2479 r = SendMessage(hwndRichEdit, EM_SCROLL,
2480 SB_PAGEUP, 0);/*page up beyond top */
2482 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2484 ok(r == 0x00010000,
2485 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2486 ok(y_before == y_after,
2487 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2489 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2490 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2491 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2492 r = SendMessage(hwndRichEdit, EM_SCROLL,
2493 SB_PAGEDOWN, 0); /* page down beyond bot */
2494 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2496 ok(r == 0x00010000,
2497 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2498 ok(y_before == y_after,
2499 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2500 y_before, y_after);
2502 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2503 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2504 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2506 ok(r == 0x00010000,
2507 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2508 ok(y_before == y_after,
2509 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2510 y_before, y_after);
2512 DestroyWindow(hwndRichEdit);
2515 static unsigned int recursionLevel = 0;
2516 static unsigned int WM_SIZE_recursionLevel = 0;
2517 static BOOL bailedOutOfRecursion = FALSE;
2518 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2520 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2522 LRESULT r;
2524 if (bailedOutOfRecursion) return 0;
2525 if (recursionLevel >= 32) {
2526 bailedOutOfRecursion = TRUE;
2527 return 0;
2530 recursionLevel++;
2531 switch (message) {
2532 case WM_SIZE:
2533 WM_SIZE_recursionLevel++;
2534 r = richeditProc(hwnd, message, wParam, lParam);
2535 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2536 ShowScrollBar(hwnd, SB_VERT, TRUE);
2537 WM_SIZE_recursionLevel--;
2538 break;
2539 default:
2540 r = richeditProc(hwnd, message, wParam, lParam);
2541 break;
2543 recursionLevel--;
2544 return r;
2547 static void test_scrollbar_visibility(void)
2549 HWND hwndRichEdit;
2550 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2551 SCROLLINFO si;
2552 WNDCLASSA cls;
2553 BOOL r;
2555 /* These tests show that richedit should temporarily refrain from automatically
2556 hiding or showing its scrollbars (vertical at least) when an explicit request
2557 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2558 Some applications depend on forced showing (when otherwise richedit would
2559 hide the vertical scrollbar) and are thrown on an endless recursive loop
2560 if richedit auto-hides the scrollbar again. Apparently they never heard of
2561 the ES_DISABLENOSCROLL style... */
2563 hwndRichEdit = new_richedit(NULL);
2565 /* Test default scrollbar visibility behavior */
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571 "Vertical scrollbar is visible, should be invisible.\n");
2572 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2573 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2574 si.nPage, si.nMin, si.nMax);
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582 "Vertical scrollbar is visible, should be invisible.\n");
2583 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2584 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2593 "Vertical scrollbar is invisible, should be visible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2599 even though it hides the scrollbar */
2600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601 memset(&si, 0, sizeof(si));
2602 si.cbSize = sizeof(si);
2603 si.fMask = SIF_PAGE | SIF_RANGE;
2604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2606 "Vertical scrollbar is visible, should be invisible.\n");
2607 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2608 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2609 si.nPage, si.nMin, si.nMax);
2611 /* Setting non-scrolling text again does *not* reset scrollbar range */
2612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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 visible, should be invisible.\n");
2619 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2620 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2621 si.nPage, si.nMin, si.nMax);
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2629 "Vertical scrollbar is visible, should be invisible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2631 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632 si.nPage, si.nMin, si.nMax);
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2635 memset(&si, 0, sizeof(si));
2636 si.cbSize = sizeof(si);
2637 si.fMask = SIF_PAGE | SIF_RANGE;
2638 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2640 "Vertical scrollbar is visible, should be invisible.\n");
2641 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2642 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2643 si.nPage, si.nMin, si.nMax);
2645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2651 "Vertical scrollbar is visible, should be invisible.\n");
2652 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2653 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2654 si.nPage, si.nMin, si.nMax);
2656 DestroyWindow(hwndRichEdit);
2658 /* Test again, with ES_DISABLENOSCROLL style */
2659 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2661 /* Test default scrollbar visibility behavior */
2662 memset(&si, 0, sizeof(si));
2663 si.cbSize = sizeof(si);
2664 si.fMask = SIF_PAGE | SIF_RANGE;
2665 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2666 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2667 "Vertical scrollbar is invisible, should be visible.\n");
2668 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2669 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2670 si.nPage, si.nMin, si.nMax);
2672 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2673 memset(&si, 0, sizeof(si));
2674 si.cbSize = sizeof(si);
2675 si.fMask = SIF_PAGE | SIF_RANGE;
2676 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2677 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2678 "Vertical scrollbar is invisible, should be visible.\n");
2679 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2680 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2681 si.nPage, si.nMin, si.nMax);
2683 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2684 memset(&si, 0, sizeof(si));
2685 si.cbSize = sizeof(si);
2686 si.fMask = SIF_PAGE | SIF_RANGE;
2687 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2689 "Vertical scrollbar is invisible, should be visible.\n");
2690 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2691 "reported page/range is %d (%d..%d)\n",
2692 si.nPage, si.nMin, si.nMax);
2694 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2695 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2696 memset(&si, 0, sizeof(si));
2697 si.cbSize = sizeof(si);
2698 si.fMask = SIF_PAGE | SIF_RANGE;
2699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2700 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2701 "Vertical scrollbar is invisible, should be visible.\n");
2702 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2703 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2704 si.nPage, si.nMin, si.nMax);
2706 /* Setting non-scrolling text again does *not* reset scrollbar range */
2707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2708 memset(&si, 0, sizeof(si));
2709 si.cbSize = sizeof(si);
2710 si.fMask = SIF_PAGE | SIF_RANGE;
2711 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2712 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2713 "Vertical scrollbar is invisible, should be visible.\n");
2714 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2715 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2716 si.nPage, si.nMin, si.nMax);
2718 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2719 memset(&si, 0, sizeof(si));
2720 si.cbSize = sizeof(si);
2721 si.fMask = SIF_PAGE | SIF_RANGE;
2722 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2723 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2724 "Vertical scrollbar is invisible, should be visible.\n");
2725 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2726 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2727 si.nPage, si.nMin, si.nMax);
2729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2730 memset(&si, 0, sizeof(si));
2731 si.cbSize = sizeof(si);
2732 si.fMask = SIF_PAGE | SIF_RANGE;
2733 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2734 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2735 "Vertical scrollbar is invisible, should be visible.\n");
2736 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2737 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2738 si.nPage, si.nMin, si.nMax);
2740 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2741 memset(&si, 0, sizeof(si));
2742 si.cbSize = sizeof(si);
2743 si.fMask = SIF_PAGE | SIF_RANGE;
2744 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746 "Vertical scrollbar is invisible, should be visible.\n");
2747 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2748 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2749 si.nPage, si.nMin, si.nMax);
2751 DestroyWindow(hwndRichEdit);
2753 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2754 hwndRichEdit = new_richedit(NULL);
2756 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2757 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2758 memset(&si, 0, sizeof(si));
2759 si.cbSize = sizeof(si);
2760 si.fMask = SIF_PAGE | SIF_RANGE;
2761 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2762 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2763 "Vertical scrollbar is invisible, should be visible.\n");
2764 todo_wine {
2765 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2766 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2767 si.nPage, si.nMin, si.nMax);
2770 /* Ditto, see above */
2771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2772 memset(&si, 0, sizeof(si));
2773 si.cbSize = sizeof(si);
2774 si.fMask = SIF_PAGE | SIF_RANGE;
2775 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2776 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2777 "Vertical scrollbar is invisible, should be visible.\n");
2778 todo_wine {
2779 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2780 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2781 si.nPage, si.nMin, si.nMax);
2784 /* Ditto, see above */
2785 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2786 memset(&si, 0, sizeof(si));
2787 si.cbSize = sizeof(si);
2788 si.fMask = SIF_PAGE | SIF_RANGE;
2789 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2790 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2791 "Vertical scrollbar is invisible, should be visible.\n");
2792 todo_wine {
2793 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2794 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2795 si.nPage, si.nMin, si.nMax);
2798 /* Ditto, see above */
2799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2800 memset(&si, 0, sizeof(si));
2801 si.cbSize = sizeof(si);
2802 si.fMask = SIF_PAGE | SIF_RANGE;
2803 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2804 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2805 "Vertical scrollbar is invisible, should be visible.\n");
2806 todo_wine {
2807 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2808 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2809 si.nPage, si.nMin, si.nMax);
2812 /* Ditto, see above */
2813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2814 memset(&si, 0, sizeof(si));
2815 si.cbSize = sizeof(si);
2816 si.fMask = SIF_PAGE | SIF_RANGE;
2817 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2818 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2819 "Vertical scrollbar is invisible, should be visible.\n");
2820 todo_wine {
2821 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2822 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2823 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2828 memset(&si, 0, sizeof(si));
2829 si.cbSize = sizeof(si);
2830 si.fMask = SIF_PAGE | SIF_RANGE;
2831 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2832 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2833 "Vertical scrollbar is visible, should be invisible.\n");
2834 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2835 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2836 si.nPage, si.nMin, si.nMax);
2838 DestroyWindow(hwndRichEdit);
2840 hwndRichEdit = new_richedit(NULL);
2842 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2843 memset(&si, 0, sizeof(si));
2844 si.cbSize = sizeof(si);
2845 si.fMask = SIF_PAGE | SIF_RANGE;
2846 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2847 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2848 "Vertical scrollbar is visible, should be invisible.\n");
2849 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2850 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2851 si.nPage, si.nMin, si.nMax);
2853 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2854 memset(&si, 0, sizeof(si));
2855 si.cbSize = sizeof(si);
2856 si.fMask = SIF_PAGE | SIF_RANGE;
2857 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2858 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2859 "Vertical scrollbar is visible, should be invisible.\n");
2860 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2861 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2862 si.nPage, si.nMin, si.nMax);
2864 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2865 memset(&si, 0, sizeof(si));
2866 si.cbSize = sizeof(si);
2867 si.fMask = SIF_PAGE | SIF_RANGE;
2868 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2869 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2870 "Vertical scrollbar is visible, should be invisible.\n");
2871 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2872 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2873 si.nPage, si.nMin, si.nMax);
2875 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2881 "Vertical scrollbar is visible, should be invisible.\n");
2882 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884 si.nPage, si.nMin, si.nMax);
2886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892 "Vertical scrollbar is invisible, should be visible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2898 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2899 memset(&si, 0, sizeof(si));
2900 si.cbSize = sizeof(si);
2901 si.fMask = SIF_PAGE | SIF_RANGE;
2902 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2904 "Vertical scrollbar is visible, should be invisible.\n");
2905 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2906 "reported page/range is %d (%d..%d)\n",
2907 si.nPage, si.nMin, si.nMax);
2909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915 "Vertical scrollbar is visible, should be invisible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d)\n",
2918 si.nPage, si.nMin, si.nMax);
2920 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2921 EM_SCROLL will make visible any forcefully invisible scrollbar */
2922 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2923 memset(&si, 0, sizeof(si));
2924 si.cbSize = sizeof(si);
2925 si.fMask = SIF_PAGE | SIF_RANGE;
2926 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2927 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2928 "Vertical scrollbar is invisible, should be visible.\n");
2929 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2930 "reported page/range is %d (%d..%d)\n",
2931 si.nPage, si.nMin, si.nMax);
2933 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2934 memset(&si, 0, sizeof(si));
2935 si.cbSize = sizeof(si);
2936 si.fMask = SIF_PAGE | SIF_RANGE;
2937 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2938 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2939 "Vertical scrollbar is visible, should be invisible.\n");
2940 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2941 "reported page/range is %d (%d..%d)\n",
2942 si.nPage, si.nMin, si.nMax);
2944 /* Again, EM_SCROLL, with SB_LINEUP */
2945 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2946 memset(&si, 0, sizeof(si));
2947 si.cbSize = sizeof(si);
2948 si.fMask = SIF_PAGE | SIF_RANGE;
2949 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2950 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2951 "Vertical scrollbar is invisible, should be visible.\n");
2952 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2953 "reported page/range is %d (%d..%d)\n",
2954 si.nPage, si.nMin, si.nMax);
2956 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2957 memset(&si, 0, sizeof(si));
2958 si.cbSize = sizeof(si);
2959 si.fMask = SIF_PAGE | SIF_RANGE;
2960 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2962 "Vertical scrollbar is visible, should be invisible.\n");
2963 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2964 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2965 si.nPage, si.nMin, si.nMax);
2967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2968 memset(&si, 0, sizeof(si));
2969 si.cbSize = sizeof(si);
2970 si.fMask = SIF_PAGE | SIF_RANGE;
2971 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2973 "Vertical scrollbar is invisible, should be visible.\n");
2974 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2975 "reported page/range is %d (%d..%d)\n",
2976 si.nPage, si.nMin, si.nMax);
2978 DestroyWindow(hwndRichEdit);
2981 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2982 hwndRichEdit = new_richedit(NULL);
2984 #define ENABLE_WS_VSCROLL(hwnd) \
2985 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2986 #define DISABLE_WS_VSCROLL(hwnd) \
2987 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2989 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2990 ENABLE_WS_VSCROLL(hwndRichEdit);
2991 memset(&si, 0, sizeof(si));
2992 si.cbSize = sizeof(si);
2993 si.fMask = SIF_PAGE | SIF_RANGE;
2994 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996 "Vertical scrollbar is invisible, should be visible.\n");
2997 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2998 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2999 si.nPage, si.nMin, si.nMax);
3001 /* Ditto, see above */
3002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3003 memset(&si, 0, sizeof(si));
3004 si.cbSize = sizeof(si);
3005 si.fMask = SIF_PAGE | SIF_RANGE;
3006 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008 "Vertical scrollbar is invisible, should be visible.\n");
3009 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3010 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3011 si.nPage, si.nMin, si.nMax);
3013 /* Ditto, see above */
3014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3015 memset(&si, 0, sizeof(si));
3016 si.cbSize = sizeof(si);
3017 si.fMask = SIF_PAGE | SIF_RANGE;
3018 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3020 "Vertical scrollbar is invisible, should be visible.\n");
3021 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3022 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3023 si.nPage, si.nMin, si.nMax);
3025 /* Ditto, see above */
3026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3027 memset(&si, 0, sizeof(si));
3028 si.cbSize = sizeof(si);
3029 si.fMask = SIF_PAGE | SIF_RANGE;
3030 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3032 "Vertical scrollbar is invisible, should be visible.\n");
3033 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035 si.nPage, si.nMin, si.nMax);
3037 /* Ditto, see above */
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 invisible, should be visible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3051 memset(&si, 0, sizeof(si));
3052 si.cbSize = sizeof(si);
3053 si.fMask = SIF_PAGE | SIF_RANGE;
3054 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3055 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3056 "Vertical scrollbar is visible, should be invisible.\n");
3057 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3058 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3059 si.nPage, si.nMin, si.nMax);
3061 DestroyWindow(hwndRichEdit);
3063 hwndRichEdit = new_richedit(NULL);
3065 DISABLE_WS_VSCROLL(hwndRichEdit);
3066 memset(&si, 0, sizeof(si));
3067 si.cbSize = sizeof(si);
3068 si.fMask = SIF_PAGE | SIF_RANGE;
3069 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3070 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3071 "Vertical scrollbar is visible, should be invisible.\n");
3072 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3073 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3074 si.nPage, si.nMin, si.nMax);
3076 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3077 memset(&si, 0, sizeof(si));
3078 si.cbSize = sizeof(si);
3079 si.fMask = SIF_PAGE | SIF_RANGE;
3080 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3081 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3082 "Vertical scrollbar is visible, should be invisible.\n");
3083 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3084 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3085 si.nPage, si.nMin, si.nMax);
3087 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3088 memset(&si, 0, sizeof(si));
3089 si.cbSize = sizeof(si);
3090 si.fMask = SIF_PAGE | SIF_RANGE;
3091 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3092 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3093 "Vertical scrollbar is visible, should be invisible.\n");
3094 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3095 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3096 si.nPage, si.nMin, si.nMax);
3098 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3099 memset(&si, 0, sizeof(si));
3100 si.cbSize = sizeof(si);
3101 si.fMask = SIF_PAGE | SIF_RANGE;
3102 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3103 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3104 "Vertical scrollbar is visible, should be invisible.\n");
3105 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3106 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3107 si.nPage, si.nMin, si.nMax);
3109 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3110 memset(&si, 0, sizeof(si));
3111 si.cbSize = sizeof(si);
3112 si.fMask = SIF_PAGE | SIF_RANGE;
3113 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3114 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3115 "Vertical scrollbar is invisible, should be visible.\n");
3116 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3117 "reported page/range is %d (%d..%d)\n",
3118 si.nPage, si.nMin, si.nMax);
3120 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3121 DISABLE_WS_VSCROLL(hwndRichEdit);
3122 memset(&si, 0, sizeof(si));
3123 si.cbSize = sizeof(si);
3124 si.fMask = SIF_PAGE | SIF_RANGE;
3125 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3126 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3127 "Vertical scrollbar is visible, should be invisible.\n");
3128 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3129 "reported page/range is %d (%d..%d)\n",
3130 si.nPage, si.nMin, si.nMax);
3132 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3133 memset(&si, 0, sizeof(si));
3134 si.cbSize = sizeof(si);
3135 si.fMask = SIF_PAGE | SIF_RANGE;
3136 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3137 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3138 "Vertical scrollbar is visible, should be invisible.\n");
3139 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3140 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3141 si.nPage, si.nMin, si.nMax);
3143 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3144 memset(&si, 0, sizeof(si));
3145 si.cbSize = sizeof(si);
3146 si.fMask = SIF_PAGE | SIF_RANGE;
3147 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3148 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3149 "Vertical scrollbar is invisible, should be visible.\n");
3150 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3151 "reported page/range is %d (%d..%d)\n",
3152 si.nPage, si.nMin, si.nMax);
3154 DISABLE_WS_VSCROLL(hwndRichEdit);
3155 memset(&si, 0, sizeof(si));
3156 si.cbSize = sizeof(si);
3157 si.fMask = SIF_PAGE | SIF_RANGE;
3158 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3159 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3160 "Vertical scrollbar is visible, should be invisible.\n");
3161 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3162 "reported page/range is %d (%d..%d)\n",
3163 si.nPage, si.nMin, si.nMax);
3165 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3166 EM_SCROLL will make visible any forcefully invisible scrollbar */
3167 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3168 memset(&si, 0, sizeof(si));
3169 si.cbSize = sizeof(si);
3170 si.fMask = SIF_PAGE | SIF_RANGE;
3171 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3172 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3173 "Vertical scrollbar is invisible, should be visible.\n");
3174 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3175 "reported page/range is %d (%d..%d)\n",
3176 si.nPage, si.nMin, si.nMax);
3178 DISABLE_WS_VSCROLL(hwndRichEdit);
3179 memset(&si, 0, sizeof(si));
3180 si.cbSize = sizeof(si);
3181 si.fMask = SIF_PAGE | SIF_RANGE;
3182 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3183 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3184 "Vertical scrollbar is visible, should be invisible.\n");
3185 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3186 "reported page/range is %d (%d..%d)\n",
3187 si.nPage, si.nMin, si.nMax);
3189 /* Again, EM_SCROLL, with SB_LINEUP */
3190 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3191 memset(&si, 0, sizeof(si));
3192 si.cbSize = sizeof(si);
3193 si.fMask = SIF_PAGE | SIF_RANGE;
3194 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3195 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3196 "Vertical scrollbar is invisible, should be visible.\n");
3197 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3198 "reported page/range is %d (%d..%d)\n",
3199 si.nPage, si.nMin, si.nMax);
3201 DestroyWindow(hwndRichEdit);
3203 /* This window proc models what is going on with Corman Lisp 3.0.
3204 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3205 force the scrollbar into visibility. Recursion should NOT happen
3206 as a result of this action.
3208 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3209 if (r) {
3210 richeditProc = cls.lpfnWndProc;
3211 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3212 cls.lpszClassName = "RicheditStupidOverride";
3213 if(!RegisterClassA(&cls)) assert(0);
3215 recursionLevel = 0;
3216 WM_SIZE_recursionLevel = 0;
3217 bailedOutOfRecursion = FALSE;
3218 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3219 ok(!bailedOutOfRecursion,
3220 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3222 recursionLevel = 0;
3223 WM_SIZE_recursionLevel = 0;
3224 bailedOutOfRecursion = FALSE;
3225 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3226 ok(!bailedOutOfRecursion,
3227 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3229 /* Unblock window in order to process WM_DESTROY */
3230 recursionLevel = 0;
3231 bailedOutOfRecursion = FALSE;
3232 WM_SIZE_recursionLevel = 0;
3233 DestroyWindow(hwndRichEdit);
3237 static void test_EM_SETUNDOLIMIT(void)
3239 /* cases we test for:
3240 * default behaviour - limiting at 100 undo's
3241 * undo disabled - setting a limit of 0
3242 * undo limited - undo limit set to some to some number, like 2
3243 * bad input - sending a negative number should default to 100 undo's */
3245 HWND hwndRichEdit = new_richedit(NULL);
3246 CHARRANGE cr;
3247 int i;
3248 int result;
3250 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3251 cr.cpMin = 0;
3252 cr.cpMax = 1;
3253 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3254 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3255 also, multiple pastes don't combine like WM_CHAR would */
3256 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3258 /* first case - check the default */
3259 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3260 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3261 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3262 for (i=0; i<100; i++) /* Undo 100 of them */
3263 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3264 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3265 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3267 /* second case - cannot undo */
3268 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3269 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3270 SendMessage(hwndRichEdit,
3271 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3272 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3273 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3275 /* third case - set it to an arbitrary number */
3276 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3277 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3278 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3279 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3280 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3281 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3282 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3283 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3284 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3285 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3286 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3287 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3288 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3289 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3291 /* fourth case - setting negative numbers should default to 100 undos */
3292 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3293 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3294 ok (result == 100,
3295 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3297 DestroyWindow(hwndRichEdit);
3300 static void test_ES_PASSWORD(void)
3302 /* This isn't hugely testable, so we're just going to run it through its paces */
3304 HWND hwndRichEdit = new_richedit(NULL);
3305 WCHAR result;
3307 /* First, check the default of a regular control */
3308 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3309 ok (result == 0,
3310 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3312 /* Now, set it to something normal */
3313 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3314 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3315 ok (result == 120,
3316 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3318 /* Now, set it to something odd */
3319 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3320 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3321 ok (result == 1234,
3322 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3323 DestroyWindow(hwndRichEdit);
3326 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3327 LPBYTE pbBuff,
3328 LONG cb,
3329 LONG *pcb)
3331 char** str = (char**)dwCookie;
3332 *pcb = cb;
3333 if (*pcb > 0) {
3334 memcpy(*str, pbBuff, *pcb);
3335 *str += *pcb;
3337 return 0;
3340 static void test_WM_SETTEXT(void)
3342 HWND hwndRichEdit = new_richedit(NULL);
3343 const char * TestItem1 = "TestSomeText";
3344 const char * TestItem2 = "TestSomeText\r";
3345 const char * TestItem2_after = "TestSomeText\r\n";
3346 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3347 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3348 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3349 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3350 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3351 const char * TestItem5_after = "TestSomeText TestSomeText";
3352 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3353 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3354 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3355 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3357 const char rtftextA[] = "{\\rtf sometext}";
3358 const char urtftextA[] = "{\\urtf sometext}";
3359 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3360 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3361 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3363 char buf[1024] = {0};
3364 WCHAR bufW[1024] = {0};
3365 LRESULT result;
3367 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3368 any solitary \r to be converted to \r\n on return. Properly paired
3369 \r\n are not affected. It also shows that the special sequence \r\r\n
3370 gets converted to a single space.
3373 #define TEST_SETTEXT(a, b) \
3374 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3375 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3376 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3377 ok (result == lstrlen(buf), \
3378 "WM_GETTEXT returned %ld instead of expected %u\n", \
3379 result, lstrlen(buf)); \
3380 result = strcmp(b, buf); \
3381 ok(result == 0, \
3382 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3384 TEST_SETTEXT(TestItem1, TestItem1)
3385 TEST_SETTEXT(TestItem2, TestItem2_after)
3386 TEST_SETTEXT(TestItem3, TestItem3_after)
3387 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3388 TEST_SETTEXT(TestItem4, TestItem4_after)
3389 TEST_SETTEXT(TestItem5, TestItem5_after)
3390 TEST_SETTEXT(TestItem6, TestItem6_after)
3391 TEST_SETTEXT(TestItem7, TestItem7_after)
3393 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3394 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3395 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3396 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3397 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3398 DestroyWindow(hwndRichEdit);
3399 #undef TEST_SETTEXT
3401 #define TEST_SETTEXTW(a, b) \
3402 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3403 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3404 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3405 ok (result == lstrlenW(bufW), \
3406 "WM_GETTEXT returned %ld instead of expected %u\n", \
3407 result, lstrlenW(bufW)); \
3408 result = lstrcmpW(b, bufW); \
3409 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3411 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3412 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3413 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3414 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3415 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3416 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3417 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3418 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3419 DestroyWindow(hwndRichEdit);
3420 #undef TEST_SETTEXTW
3423 static void test_EM_STREAMOUT(void)
3425 HWND hwndRichEdit = new_richedit(NULL);
3426 int r;
3427 EDITSTREAM es;
3428 char buf[1024] = {0};
3429 char * p;
3431 const char * TestItem1 = "TestSomeText";
3432 const char * TestItem2 = "TestSomeText\r";
3433 const char * TestItem3 = "TestSomeText\r\n";
3435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3436 p = buf;
3437 es.dwCookie = (DWORD_PTR)&p;
3438 es.dwError = 0;
3439 es.pfnCallback = test_WM_SETTEXT_esCallback;
3440 memset(buf, 0, sizeof(buf));
3441 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3442 r = strlen(buf);
3443 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3444 ok(strcmp(buf, TestItem1) == 0,
3445 "streamed text different, got %s\n", buf);
3447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3448 p = buf;
3449 es.dwCookie = (DWORD_PTR)&p;
3450 es.dwError = 0;
3451 es.pfnCallback = test_WM_SETTEXT_esCallback;
3452 memset(buf, 0, sizeof(buf));
3453 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3454 r = strlen(buf);
3455 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3456 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3457 ok(strcmp(buf, TestItem3) == 0,
3458 "streamed text different from, got %s\n", buf);
3459 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3460 p = buf;
3461 es.dwCookie = (DWORD_PTR)&p;
3462 es.dwError = 0;
3463 es.pfnCallback = test_WM_SETTEXT_esCallback;
3464 memset(buf, 0, sizeof(buf));
3465 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3466 r = strlen(buf);
3467 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3468 ok(strcmp(buf, TestItem3) == 0,
3469 "streamed text different, got %s\n", buf);
3471 DestroyWindow(hwndRichEdit);
3474 static void test_EM_STREAMOUT_FONTTBL(void)
3476 HWND hwndRichEdit = new_richedit(NULL);
3477 EDITSTREAM es;
3478 char buf[1024] = {0};
3479 char * p;
3480 char * fontTbl;
3481 int brackCount;
3483 const char * TestItem = "TestSomeText";
3485 /* fills in the richedit control with some text */
3486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3488 /* streams out the text in rtf format */
3489 p = buf;
3490 es.dwCookie = (DWORD_PTR)&p;
3491 es.dwError = 0;
3492 es.pfnCallback = test_WM_SETTEXT_esCallback;
3493 memset(buf, 0, sizeof(buf));
3494 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3496 /* scans for \fonttbl, error if not found */
3497 fontTbl = strstr(buf, "\\fonttbl");
3498 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3499 if(fontTbl)
3501 /* scans for terminating closing bracket */
3502 brackCount = 1;
3503 while(*fontTbl && brackCount)
3505 if(*fontTbl == '{')
3506 brackCount++;
3507 else if(*fontTbl == '}')
3508 brackCount--;
3509 fontTbl++;
3511 /* checks whether closing bracket is ok */
3512 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3513 if(!brackCount)
3515 /* char before closing fonttbl block should be a closed bracket */
3516 fontTbl -= 2;
3517 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3519 /* char after fonttbl block should be a crlf */
3520 fontTbl += 2;
3521 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3524 DestroyWindow(hwndRichEdit);
3528 static void test_EM_SETTEXTEX(void)
3530 HWND hwndRichEdit, parent;
3531 SCROLLINFO si;
3532 int sel_start, sel_end;
3533 SETTEXTEX setText;
3534 GETTEXTEX getText;
3535 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3536 'S', 'o', 'm', 'e',
3537 'T', 'e', 'x', 't', 0};
3538 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3539 't', 'S', 'o', 'm',
3540 'e', 'T', 'e', 'x',
3541 't', 't', 'S', 'o',
3542 'm', 'e', 'T', 'e',
3543 'x', 't', 0};
3544 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3545 '\r','t','S','o','m','e','T','e','x','t',0};
3546 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3547 'S', 'o', 'm', 'e',
3548 'T', 'e', 'x', 't',
3549 '\r', 0};
3550 const char * TestItem2_after = "TestSomeText\r\n";
3551 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3552 'S', 'o', 'm', 'e',
3553 'T', 'e', 'x', 't',
3554 '\r','\n','\r','\n', 0};
3555 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3556 'S', 'o', 'm', 'e',
3557 'T', 'e', 'x', 't',
3558 '\n','\n', 0};
3559 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3560 'S', 'o', 'm', 'e',
3561 'T', 'e', 'x', 't',
3562 '\r','\r', 0};
3563 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3564 'S', 'o', 'm', 'e',
3565 'T', 'e', 'x', 't',
3566 '\r','\r','\n','\r',
3567 '\n', 0};
3568 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3569 'S', 'o', 'm', 'e',
3570 'T', 'e', 'x', 't',
3571 ' ','\r', 0};
3572 #define MAX_BUF_LEN 1024
3573 WCHAR buf[MAX_BUF_LEN];
3574 char bufACP[MAX_BUF_LEN];
3575 char * p;
3576 int result;
3577 CHARRANGE cr;
3578 EDITSTREAM es;
3579 WNDCLASSA cls;
3581 /* Test the scroll position with and without a parent window.
3583 * For some reason the scroll position is 0 after EM_SETTEXTEX
3584 * with the ST_SELECTION flag only when the control has a parent
3585 * window, even though the selection is at the end. */
3586 cls.style = 0;
3587 cls.lpfnWndProc = DefWindowProcA;
3588 cls.cbClsExtra = 0;
3589 cls.cbWndExtra = 0;
3590 cls.hInstance = GetModuleHandleA(0);
3591 cls.hIcon = 0;
3592 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3593 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3594 cls.lpszMenuName = NULL;
3595 cls.lpszClassName = "ParentTestClass";
3596 if(!RegisterClassA(&cls)) assert(0);
3598 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3599 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3600 ok (parent != 0, "Failed to create parent window\n");
3602 hwndRichEdit = CreateWindowEx(0,
3603 RICHEDIT_CLASS, NULL,
3604 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3605 0, 0, 200, 60, parent, NULL,
3606 hmoduleRichEdit, NULL);
3608 setText.codepage = CP_ACP;
3609 setText.flags = ST_SELECTION;
3610 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3611 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3612 si.cbSize = sizeof(si);
3613 si.fMask = SIF_ALL;
3614 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3615 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3616 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3617 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3618 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3620 DestroyWindow(parent);
3622 /* Test without a parent window */
3623 hwndRichEdit = new_richedit(NULL);
3624 setText.codepage = CP_ACP;
3625 setText.flags = ST_SELECTION;
3626 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3627 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3628 si.cbSize = sizeof(si);
3629 si.fMask = SIF_ALL;
3630 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3631 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3632 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3633 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3634 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3636 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3637 * but this time it is because the selection is at the beginning. */
3638 setText.codepage = CP_ACP;
3639 setText.flags = ST_DEFAULT;
3640 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3641 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3642 si.cbSize = sizeof(si);
3643 si.fMask = SIF_ALL;
3644 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3645 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3646 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3647 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3648 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3650 setText.codepage = 1200; /* no constant for unicode */
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.cb = MAX_BUF_LEN;
3653 getText.flags = GT_DEFAULT;
3654 getText.lpDefaultChar = NULL;
3655 getText.lpUsedDefChar = NULL;
3657 setText.flags = 0;
3658 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3659 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3660 ok(lstrcmpW(buf, TestItem1) == 0,
3661 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3663 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3664 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3666 setText.codepage = 1200; /* no constant for unicode */
3667 getText.codepage = 1200; /* no constant for unicode */
3668 getText.cb = MAX_BUF_LEN;
3669 getText.flags = GT_DEFAULT;
3670 getText.lpDefaultChar = NULL;
3671 getText.lpUsedDefChar = NULL;
3672 setText.flags = 0;
3673 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3674 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3675 ok(lstrcmpW(buf, TestItem2) == 0,
3676 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3678 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3679 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3680 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3681 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3683 /* Baseline test for just-enough buffer space for string */
3684 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3685 getText.codepage = 1200; /* no constant for unicode */
3686 getText.flags = GT_DEFAULT;
3687 getText.lpDefaultChar = NULL;
3688 getText.lpUsedDefChar = NULL;
3689 memset(buf, 0, MAX_BUF_LEN);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3691 ok(lstrcmpW(buf, TestItem2) == 0,
3692 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3694 /* When there is enough space for one character, but not both, of the CRLF
3695 pair at the end of the string, the CR is not copied at all. That is,
3696 the caller must not see CRLF pairs truncated to CR at the end of the
3697 string.
3699 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3700 getText.codepage = 1200; /* no constant for unicode */
3701 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3702 getText.lpDefaultChar = NULL;
3703 getText.lpUsedDefChar = NULL;
3704 memset(buf, 0, MAX_BUF_LEN);
3705 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3706 ok(lstrcmpW(buf, TestItem1) == 0,
3707 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3710 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3711 setText.codepage = 1200; /* no constant for unicode */
3712 getText.codepage = 1200; /* no constant for unicode */
3713 getText.cb = MAX_BUF_LEN;
3714 getText.flags = GT_DEFAULT;
3715 getText.lpDefaultChar = NULL;
3716 getText.lpUsedDefChar = NULL;
3717 setText.flags = 0;
3718 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3719 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3720 ok(lstrcmpW(buf, TestItem3_after) == 0,
3721 "EM_SETTEXTEX did not convert properly\n");
3723 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3724 setText.codepage = 1200; /* no constant for unicode */
3725 getText.codepage = 1200; /* no constant for unicode */
3726 getText.cb = MAX_BUF_LEN;
3727 getText.flags = GT_DEFAULT;
3728 getText.lpDefaultChar = NULL;
3729 getText.lpUsedDefChar = NULL;
3730 setText.flags = 0;
3731 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3732 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3733 ok(lstrcmpW(buf, TestItem3_after) == 0,
3734 "EM_SETTEXTEX did not convert properly\n");
3736 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3737 setText.codepage = 1200; /* no constant for unicode */
3738 getText.codepage = 1200; /* no constant for unicode */
3739 getText.cb = MAX_BUF_LEN;
3740 getText.flags = GT_DEFAULT;
3741 getText.lpDefaultChar = NULL;
3742 getText.lpUsedDefChar = NULL;
3743 setText.flags = 0;
3744 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3745 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3746 ok(lstrcmpW(buf, TestItem4_after) == 0,
3747 "EM_SETTEXTEX did not convert properly\n");
3749 /* !ST_SELECTION && Unicode && !\rtf */
3750 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3751 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3753 ok (result == 1,
3754 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3755 ok(lstrlenW(buf) == 0,
3756 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3758 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3759 setText.flags = 0;
3760 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3761 /* select some text */
3762 cr.cpMax = 1;
3763 cr.cpMin = 3;
3764 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3765 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3766 setText.flags = ST_SELECTION;
3767 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3768 ok(result == 0,
3769 "EM_SETTEXTEX with NULL lParam to replace selection"
3770 " with no text should return 0. Got %i\n",
3771 result);
3773 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3774 setText.flags = 0;
3775 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3776 /* select some text */
3777 cr.cpMax = 1;
3778 cr.cpMin = 3;
3779 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3780 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3781 setText.flags = ST_SELECTION;
3782 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3783 (WPARAM)&setText, (LPARAM) TestItem1);
3784 /* get text */
3785 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3786 ok(result == lstrlenW(TestItem1),
3787 "EM_SETTEXTEX with NULL lParam to replace selection"
3788 " with no text should return 0. Got %i\n",
3789 result);
3790 ok(lstrlenW(buf) == 22,
3791 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3792 lstrlenW(buf) );
3794 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3795 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3796 p = (char *)buf;
3797 es.dwCookie = (DWORD_PTR)&p;
3798 es.dwError = 0;
3799 es.pfnCallback = test_WM_SETTEXT_esCallback;
3800 memset(buf, 0, sizeof(buf));
3801 SendMessage(hwndRichEdit, EM_STREAMOUT,
3802 (WPARAM)(SF_RTF), (LPARAM)&es);
3803 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3805 /* !ST_SELECTION && !Unicode && \rtf */
3806 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3807 getText.codepage = 1200; /* no constant for unicode */
3808 getText.cb = MAX_BUF_LEN;
3809 getText.flags = GT_DEFAULT;
3810 getText.lpDefaultChar = NULL;
3811 getText.lpUsedDefChar = NULL;
3813 setText.flags = 0;
3814 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3815 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3816 ok(lstrcmpW(buf, TestItem1) == 0,
3817 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3819 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3820 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3821 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3822 getText.codepage = CP_ACP;
3823 getText.cb = MAX_BUF_LEN;
3824 getText.flags = GT_DEFAULT;
3825 getText.lpDefaultChar = NULL;
3826 getText.lpUsedDefChar = NULL;
3828 setText.flags = ST_SELECTION;
3829 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3830 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3831 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3832 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3833 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3835 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3837 p = (char *)buf;
3838 es.dwCookie = (DWORD_PTR)&p;
3839 es.dwError = 0;
3840 es.pfnCallback = test_WM_SETTEXT_esCallback;
3841 memset(buf, 0, sizeof(buf));
3842 SendMessage(hwndRichEdit, EM_STREAMOUT,
3843 (WPARAM)(SF_RTF), (LPARAM)&es);
3844 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3846 /* select some text */
3847 cr.cpMax = 1;
3848 cr.cpMin = 3;
3849 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3851 /* ST_SELECTION && !Unicode && \rtf */
3852 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3853 getText.codepage = 1200; /* no constant for unicode */
3854 getText.cb = MAX_BUF_LEN;
3855 getText.flags = GT_DEFAULT;
3856 getText.lpDefaultChar = NULL;
3857 getText.lpUsedDefChar = NULL;
3859 setText.flags = ST_SELECTION;
3860 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3861 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3862 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3864 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3865 setText.codepage = 1200; /* no constant for unicode */
3866 getText.codepage = CP_ACP;
3867 getText.cb = MAX_BUF_LEN;
3869 setText.flags = 0;
3870 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3871 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3873 /* select some text */
3874 cr.cpMax = 1;
3875 cr.cpMin = 3;
3876 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3878 /* ST_SELECTION && !Unicode && !\rtf */
3879 setText.codepage = CP_ACP;
3880 getText.codepage = 1200; /* no constant for unicode */
3881 getText.cb = MAX_BUF_LEN;
3882 getText.flags = GT_DEFAULT;
3883 getText.lpDefaultChar = NULL;
3884 getText.lpUsedDefChar = NULL;
3886 setText.flags = ST_SELECTION;
3887 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3888 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3889 ok(lstrcmpW(buf, TestItem1alt) == 0,
3890 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3891 " using ST_SELECTION and non-Unicode\n");
3893 /* Test setting text using rich text format */
3894 setText.flags = 0;
3895 setText.codepage = CP_ACP;
3896 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3897 getText.codepage = CP_ACP;
3898 getText.cb = MAX_BUF_LEN;
3899 getText.flags = GT_DEFAULT;
3900 getText.lpDefaultChar = NULL;
3901 getText.lpUsedDefChar = NULL;
3902 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3903 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3905 setText.flags = 0;
3906 setText.codepage = CP_ACP;
3907 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3908 getText.codepage = CP_ACP;
3909 getText.cb = MAX_BUF_LEN;
3910 getText.flags = GT_DEFAULT;
3911 getText.lpDefaultChar = NULL;
3912 getText.lpUsedDefChar = NULL;
3913 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3914 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3916 /* test for utf8 text with BOM */
3917 setText.flags = 0;
3918 setText.codepage = CP_ACP;
3919 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3920 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3921 todo_wine ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3922 result = strcmp(bufACP, "TestUTF8WithBOM");
3923 todo_wine ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3925 setText.flags = 0;
3926 setText.codepage = CP_UTF8;
3927 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3928 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3929 todo_wine ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3930 result = strcmp(bufACP, "TestUTF8WithBOM");
3931 todo_wine ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3933 DestroyWindow(hwndRichEdit);
3936 static void test_EM_LIMITTEXT(void)
3938 int ret;
3940 HWND hwndRichEdit = new_richedit(NULL);
3942 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3943 * about setting the length to -1 for multiline edit controls doesn't happen.
3946 /* Don't check default gettextlimit case. That's done in other tests */
3948 /* Set textlimit to 100 */
3949 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3950 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3951 ok (ret == 100,
3952 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3954 /* Set textlimit to 0 */
3955 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3956 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3957 ok (ret == 65536,
3958 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3960 /* Set textlimit to -1 */
3961 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3962 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3963 ok (ret == -1,
3964 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3966 /* Set textlimit to -2 */
3967 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3968 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3969 ok (ret == -2,
3970 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3972 DestroyWindow (hwndRichEdit);
3976 static void test_EM_EXLIMITTEXT(void)
3978 int i, selBegin, selEnd, len1, len2;
3979 int result;
3980 char text[1024 + 1];
3981 char buffer[1024 + 1];
3982 int textlimit = 0; /* multiple of 100 */
3983 HWND hwndRichEdit = new_richedit(NULL);
3985 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3986 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3988 textlimit = 256000;
3989 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3990 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3991 /* set higher */
3992 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3994 textlimit = 1000;
3995 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3996 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3997 /* set lower */
3998 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4000 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4001 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4002 /* default for WParam = 0 */
4003 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4005 textlimit = sizeof(text)-1;
4006 memset(text, 'W', textlimit);
4007 text[sizeof(text)-1] = 0;
4008 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4009 /* maxed out text */
4010 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4012 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4013 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4014 len1 = selEnd - selBegin;
4016 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4017 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4018 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4019 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4020 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4021 len2 = selEnd - selBegin;
4023 ok(len1 != len2,
4024 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4025 len1,len2,i);
4027 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4028 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4029 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
4030 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4031 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4032 len1 = selEnd - selBegin;
4034 ok(len1 != len2,
4035 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4036 len1,len2,i);
4038 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4039 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4040 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4041 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4042 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4043 len2 = selEnd - selBegin;
4045 ok(len1 == len2,
4046 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4047 len1,len2,i);
4049 /* set text up to the limit, select all the text, then add a char */
4050 textlimit = 5;
4051 memset(text, 'W', textlimit);
4052 text[textlimit] = 0;
4053 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4054 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4055 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4056 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4057 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4058 result = strcmp(buffer, "A");
4059 ok(0 == result, "got string = \"%s\"\n", buffer);
4061 /* WM_SETTEXT not limited */
4062 textlimit = 10;
4063 memset(text, 'W', textlimit);
4064 text[textlimit] = 0;
4065 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4066 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4067 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4068 i = strlen(buffer);
4069 ok(10 == i, "expected 10 chars\n");
4070 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4071 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4073 /* try inserting more text at end */
4074 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4075 ok(0 == i, "WM_CHAR wasn't processed\n");
4076 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4077 i = strlen(buffer);
4078 ok(10 == i, "expected 10 chars, got %i\n", i);
4079 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4080 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4082 /* try inserting text at beginning */
4083 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4084 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4085 ok(0 == i, "WM_CHAR wasn't processed\n");
4086 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4087 i = strlen(buffer);
4088 ok(10 == i, "expected 10 chars, got %i\n", i);
4089 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4090 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4092 /* WM_CHAR is limited */
4093 textlimit = 1;
4094 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4095 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4096 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4097 ok(0 == i, "WM_CHAR wasn't processed\n");
4098 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4099 ok(0 == i, "WM_CHAR wasn't processed\n");
4100 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4101 i = strlen(buffer);
4102 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4104 DestroyWindow(hwndRichEdit);
4107 static void test_EM_GETLIMITTEXT(void)
4109 int i;
4110 HWND hwndRichEdit = new_richedit(NULL);
4112 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4113 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4115 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4116 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4117 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4119 DestroyWindow(hwndRichEdit);
4122 static void test_WM_SETFONT(void)
4124 /* There is no invalid input or error conditions for this function.
4125 * NULL wParam and lParam just fall back to their default values
4126 * It should be noted that even if you use a gibberish name for your fonts
4127 * here, it will still work because the name is stored. They will display as
4128 * System, but will report their name to be whatever they were created as */
4130 HWND hwndRichEdit = new_richedit(NULL);
4131 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4132 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4133 FF_DONTCARE, "Marlett");
4134 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4135 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4136 FF_DONTCARE, "MS Sans Serif");
4137 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4138 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4139 FF_DONTCARE, "Courier");
4140 LOGFONTA sentLogFont;
4141 CHARFORMAT2A returnedCF2A;
4143 returnedCF2A.cbSize = sizeof(returnedCF2A);
4145 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4146 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4147 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4149 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4150 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4151 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4152 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4154 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4155 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4156 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4157 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4158 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4159 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4161 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4162 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4163 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4164 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4165 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4166 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4168 /* This last test is special since we send in NULL. We clear the variables
4169 * and just compare to "System" instead of the sent in font name. */
4170 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4171 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4172 returnedCF2A.cbSize = sizeof(returnedCF2A);
4174 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4175 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4176 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4177 ok (!strcmp("System",returnedCF2A.szFaceName),
4178 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4180 DestroyWindow(hwndRichEdit);
4184 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4185 LPBYTE pbBuff,
4186 LONG cb,
4187 LONG *pcb)
4189 const char** str = (const char**)dwCookie;
4190 int size = strlen(*str);
4191 if(size > 3) /* let's make it piecemeal for fun */
4192 size = 3;
4193 *pcb = cb;
4194 if (*pcb > size) {
4195 *pcb = size;
4197 if (*pcb > 0) {
4198 memcpy(pbBuff, *str, *pcb);
4199 *str += *pcb;
4201 return 0;
4204 static void test_EM_GETMODIFY(void)
4206 HWND hwndRichEdit = new_richedit(NULL);
4207 LRESULT result;
4208 SETTEXTEX setText;
4209 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4210 'S', 'o', 'm', 'e',
4211 'T', 'e', 'x', 't', 0};
4212 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4213 'S', 'o', 'm', 'e',
4214 'O', 't', 'h', 'e', 'r',
4215 'T', 'e', 'x', 't', 0};
4216 const char* streamText = "hello world";
4217 CHARFORMAT2 cf2;
4218 PARAFORMAT2 pf2;
4219 EDITSTREAM es;
4221 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4222 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4223 FF_DONTCARE, "Courier");
4225 setText.codepage = 1200; /* no constant for unicode */
4226 setText.flags = ST_KEEPUNDO;
4229 /* modify flag shouldn't be set when richedit is first created */
4230 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4231 ok (result == 0,
4232 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4234 /* setting modify flag should actually set it */
4235 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4237 ok (result != 0,
4238 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4240 /* clearing modify flag should actually clear it */
4241 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4243 ok (result == 0,
4244 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4246 /* setting font doesn't change modify flag */
4247 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4248 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4249 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4250 ok (result == 0,
4251 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4253 /* setting text should set modify flag */
4254 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4255 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4256 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4257 ok (result != 0,
4258 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4260 /* undo previous text doesn't reset modify flag */
4261 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4263 ok (result != 0,
4264 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4266 /* set text with no flag to keep undo stack should not set modify flag */
4267 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4268 setText.flags = 0;
4269 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4270 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4271 ok (result == 0,
4272 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4274 /* WM_SETTEXT doesn't modify */
4275 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4276 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4277 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4278 ok (result == 0,
4279 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4281 /* clear the text */
4282 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4283 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4284 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4285 ok (result == 0,
4286 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4288 /* replace text */
4289 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4290 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4291 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4292 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4293 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4294 ok (result != 0,
4295 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4297 /* copy/paste text 1 */
4298 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4299 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4300 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4301 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4302 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4303 ok (result != 0,
4304 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4306 /* copy/paste text 2 */
4307 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4308 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4309 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4310 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4311 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4312 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4313 ok (result != 0,
4314 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4316 /* press char */
4317 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4318 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4319 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4320 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4321 ok (result != 0,
4322 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4324 /* press del */
4325 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4326 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4327 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4328 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4329 ok (result != 0,
4330 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4332 /* set char format */
4333 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4334 cf2.cbSize = sizeof(CHARFORMAT2);
4335 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4336 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4337 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4338 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4339 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4340 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4341 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4342 ok (result != 0,
4343 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4345 /* set para format */
4346 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4347 pf2.cbSize = sizeof(PARAFORMAT2);
4348 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4349 (LPARAM) &pf2);
4350 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4351 pf2.wAlignment = PFA_RIGHT;
4352 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4353 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4354 ok (result == 0,
4355 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4357 /* EM_STREAM */
4358 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4359 es.dwCookie = (DWORD_PTR)&streamText;
4360 es.dwError = 0;
4361 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4362 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4363 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4364 ok (result != 0,
4365 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4367 DestroyWindow(hwndRichEdit);
4370 struct exsetsel_s {
4371 LONG min;
4372 LONG max;
4373 LRESULT expected_retval;
4374 int expected_getsel_start;
4375 int expected_getsel_end;
4376 int _getsel_todo_wine;
4379 const struct exsetsel_s exsetsel_tests[] = {
4380 /* sanity tests */
4381 {5, 10, 10, 5, 10, 0},
4382 {15, 17, 17, 15, 17, 0},
4383 /* test cpMax > strlen() */
4384 {0, 100, 18, 0, 18, 1},
4385 /* test cpMin == cpMax */
4386 {5, 5, 5, 5, 5, 0},
4387 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4388 {-1, 0, 5, 5, 5, 0},
4389 {-1, 17, 5, 5, 5, 0},
4390 {-1, 18, 5, 5, 5, 0},
4391 /* test cpMin < 0 && cpMax < 0 */
4392 {-1, -1, 17, 17, 17, 0},
4393 {-4, -5, 17, 17, 17, 0},
4394 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4395 {0, -1, 18, 0, 18, 1},
4396 {17, -5, 18, 17, 18, 1},
4397 {18, -3, 17, 17, 17, 0},
4398 /* test if cpMin > cpMax */
4399 {15, 19, 18, 15, 18, 1},
4400 {19, 15, 18, 15, 18, 1}
4403 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4404 CHARRANGE cr;
4405 LRESULT result;
4406 int start, end;
4408 cr.cpMin = setsel->min;
4409 cr.cpMax = setsel->max;
4410 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4412 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4414 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4416 if (setsel->_getsel_todo_wine) {
4417 todo_wine {
4418 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);
4420 } else {
4421 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);
4425 static void test_EM_EXSETSEL(void)
4427 HWND hwndRichEdit = new_richedit(NULL);
4428 int i;
4429 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4431 /* sending some text to the window */
4432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4433 /* 01234567890123456*/
4434 /* 10 */
4436 for (i = 0; i < num_tests; i++) {
4437 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4440 DestroyWindow(hwndRichEdit);
4443 static void test_EM_REPLACESEL(int redraw)
4445 HWND hwndRichEdit = new_richedit(NULL);
4446 char buffer[1024] = {0};
4447 int r;
4448 GETTEXTEX getText;
4449 CHARRANGE cr;
4451 /* sending some text to the window */
4452 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4453 /* 01234567890123456*/
4454 /* 10 */
4456 /* FIXME add more tests */
4457 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4458 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4459 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4460 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4461 r = strcmp(buffer, "testing");
4462 ok(0 == r, "expected %d, got %d\n", 0, r);
4464 DestroyWindow(hwndRichEdit);
4466 hwndRichEdit = new_richedit(NULL);
4468 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4469 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4471 /* Test behavior with carriage returns and newlines */
4472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4473 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4474 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4475 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4476 r = strcmp(buffer, "RichEdit1");
4477 ok(0 == r, "expected %d, got %d\n", 0, r);
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, "RichEdit1") == 0,
4485 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4487 /* Test number of lines reported after EM_REPLACESEL */
4488 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4489 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4491 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4492 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4493 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4494 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4495 r = strcmp(buffer, "RichEdit1\r\n");
4496 ok(0 == r, "expected %d, got %d\n", 0, r);
4497 getText.cb = 1024;
4498 getText.codepage = CP_ACP;
4499 getText.flags = GT_DEFAULT;
4500 getText.lpDefaultChar = NULL;
4501 getText.lpUsedDefChar = NULL;
4502 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4503 ok(strcmp(buffer, "RichEdit1\r") == 0,
4504 "EM_GETTEXTEX returned incorrect string\n");
4506 /* Test number of lines reported after EM_REPLACESEL */
4507 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4508 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4510 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4511 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4512 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4514 /* Test number of lines reported after EM_REPLACESEL */
4515 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4516 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4518 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4519 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4520 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4521 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4523 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4524 r = strcmp(buffer, "RichEdit1\r\n");
4525 ok(0 == r, "expected %d, got %d\n", 0, r);
4526 getText.cb = 1024;
4527 getText.codepage = CP_ACP;
4528 getText.flags = GT_DEFAULT;
4529 getText.lpDefaultChar = NULL;
4530 getText.lpUsedDefChar = NULL;
4531 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4532 ok(strcmp(buffer, "RichEdit1\r") == 0,
4533 "EM_GETTEXTEX returned incorrect string\n");
4535 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4536 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4537 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4538 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4540 /* The following tests show that richedit should handle the special \r\r\n
4541 sequence by turning it into a single space on insertion. However,
4542 EM_REPLACESEL on WinXP returns the number of characters in the original
4543 string.
4546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4547 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4548 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4549 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4550 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4551 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4552 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4554 /* Test the actual string */
4555 getText.cb = 1024;
4556 getText.codepage = CP_ACP;
4557 getText.flags = GT_DEFAULT;
4558 getText.lpDefaultChar = NULL;
4559 getText.lpUsedDefChar = NULL;
4560 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4561 ok(strcmp(buffer, "\r\r") == 0,
4562 "EM_GETTEXTEX returned incorrect string\n");
4564 /* Test number of lines reported after EM_REPLACESEL */
4565 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4566 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4568 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4569 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4570 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4571 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4572 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4573 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4574 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4576 /* Test the actual string */
4577 getText.cb = 1024;
4578 getText.codepage = CP_ACP;
4579 getText.flags = GT_DEFAULT;
4580 getText.lpDefaultChar = NULL;
4581 getText.lpUsedDefChar = NULL;
4582 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4583 ok(strcmp(buffer, " ") == 0,
4584 "EM_GETTEXTEX returned incorrect string\n");
4586 /* Test number of lines reported after EM_REPLACESEL */
4587 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4588 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4591 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4592 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4593 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4594 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4595 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4596 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4598 /* Test the actual string */
4599 getText.cb = 1024;
4600 getText.codepage = CP_ACP;
4601 getText.flags = GT_DEFAULT;
4602 getText.lpDefaultChar = NULL;
4603 getText.lpUsedDefChar = NULL;
4604 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4605 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4606 "EM_GETTEXTEX returned incorrect string\n");
4608 /* Test number of lines reported after EM_REPLACESEL */
4609 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4610 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4613 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4614 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4615 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4616 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4617 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4618 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4620 /* Test the actual string */
4621 getText.cb = 1024;
4622 getText.codepage = CP_ACP;
4623 getText.flags = GT_DEFAULT;
4624 getText.lpDefaultChar = NULL;
4625 getText.lpUsedDefChar = NULL;
4626 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4627 ok(strcmp(buffer, " \r") == 0,
4628 "EM_GETTEXTEX returned incorrect string\n");
4630 /* Test number of lines reported after EM_REPLACESEL */
4631 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4632 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4635 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4636 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4637 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4638 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4639 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4640 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4642 /* Test the actual string */
4643 getText.cb = 1024;
4644 getText.codepage = CP_ACP;
4645 getText.flags = GT_DEFAULT;
4646 getText.lpDefaultChar = NULL;
4647 getText.lpUsedDefChar = NULL;
4648 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4649 ok(strcmp(buffer, " \r\r") == 0,
4650 "EM_GETTEXTEX returned incorrect string\n");
4652 /* Test number of lines reported after EM_REPLACESEL */
4653 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4654 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4656 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4657 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4658 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4659 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4660 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4661 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4662 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4664 /* Test the actual string */
4665 getText.cb = 1024;
4666 getText.codepage = CP_ACP;
4667 getText.flags = GT_DEFAULT;
4668 getText.lpDefaultChar = NULL;
4669 getText.lpUsedDefChar = NULL;
4670 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4671 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4672 "EM_GETTEXTEX returned incorrect string\n");
4674 /* Test number of lines reported after EM_REPLACESEL */
4675 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4676 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4678 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4679 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4680 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4681 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4682 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4683 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4684 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4686 /* Test the actual string */
4687 getText.cb = 1024;
4688 getText.codepage = CP_ACP;
4689 getText.flags = GT_DEFAULT;
4690 getText.lpDefaultChar = NULL;
4691 getText.lpUsedDefChar = NULL;
4692 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4693 ok(strcmp(buffer, "\r\r") == 0,
4694 "EM_GETTEXTEX returned incorrect string\n");
4696 /* Test number of lines reported after EM_REPLACESEL */
4697 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4698 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4700 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4701 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4702 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4703 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4704 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4705 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4706 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4708 /* Test the actual string */
4709 getText.cb = 1024;
4710 getText.codepage = CP_ACP;
4711 getText.flags = GT_DEFAULT;
4712 getText.lpDefaultChar = NULL;
4713 getText.lpUsedDefChar = NULL;
4714 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4715 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4716 "EM_GETTEXTEX returned incorrect string\n");
4718 /* Test number of lines reported after EM_REPLACESEL */
4719 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4720 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4722 if (!redraw)
4723 /* This is needed to avoid interferring with keybd_event calls
4724 * on other tests that simulate keyboard events. */
4725 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4727 DestroyWindow(hwndRichEdit);
4730 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4731 * to test the state of the modifiers (Ctrl/Alt/Shift).
4733 * Therefore Ctrl-<key> keystrokes need to be simulated with
4734 * keybd_event or by using SetKeyboardState to set the modifiers
4735 * and SendMessage to simulate the keystrokes.
4737 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4739 LRESULT result;
4740 hold_key(VK_CONTROL);
4741 result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4742 release_key(VK_CONTROL);
4743 return result;
4746 static void test_WM_PASTE(void)
4748 int result;
4749 char buffer[1024] = {0};
4750 const char* text1 = "testing paste\r";
4751 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4752 const char* text1_after = "testing paste\r\n";
4753 const char* text2 = "testing paste\r\rtesting paste";
4754 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4755 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4756 HWND hwndRichEdit = new_richedit(NULL);
4758 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4759 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4761 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4762 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4763 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4764 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4765 /* Pasted text should be visible at this step */
4766 result = strcmp(text1_step1, buffer);
4767 ok(result == 0,
4768 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4770 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 /* Text should be the same as before (except for \r -> \r\n conversion) */
4773 result = strcmp(text1_after, buffer);
4774 ok(result == 0,
4775 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4778 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4779 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4780 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4781 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4782 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4783 /* Pasted text should be visible at this step */
4784 result = strcmp(text3, buffer);
4785 ok(result == 0,
4786 "test paste: strcmp = %i\n", result);
4787 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4788 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4789 /* Text should be the same as before (except for \r -> \r\n conversion) */
4790 result = strcmp(text2_after, buffer);
4791 ok(result == 0,
4792 "test paste: strcmp = %i\n", result);
4793 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4794 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4795 /* Text should revert to post-paste state */
4796 result = strcmp(buffer,text3);
4797 ok(result == 0,
4798 "test paste: strcmp = %i\n", result);
4800 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4801 /* Send WM_CHAR to simulates Ctrl-V */
4802 SendMessage(hwndRichEdit, WM_CHAR, 22,
4803 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4804 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4805 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4806 result = strcmp(buffer,"");
4807 ok(result == 0,
4808 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4810 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4811 * with SetKeyboard state. */
4813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4814 /* Simulates paste (Ctrl-V) */
4815 hold_key(VK_CONTROL);
4816 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4817 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4818 release_key(VK_CONTROL);
4819 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4820 result = strcmp(buffer,"paste");
4821 ok(result == 0,
4822 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4824 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4825 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4826 /* Simulates copy (Ctrl-C) */
4827 hold_key(VK_CONTROL);
4828 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4829 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4830 release_key(VK_CONTROL);
4831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4832 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4833 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4834 result = strcmp(buffer,"testing");
4835 ok(result == 0,
4836 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4838 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4840 /* Simulates select all (Ctrl-A) */
4841 hold_key(VK_CONTROL);
4842 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4843 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4844 /* Simulates select cut (Ctrl-X) */
4845 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4846 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4847 release_key(VK_CONTROL);
4848 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4849 result = strcmp(buffer,"");
4850 ok(result == 0,
4851 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4852 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4853 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4854 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4855 result = strcmp(buffer,"cut\r\n");
4856 todo_wine ok(result == 0,
4857 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4858 /* Simulates undo (Ctrl-Z) */
4859 hold_key(VK_CONTROL);
4860 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4861 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4862 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4863 result = strcmp(buffer,"");
4864 ok(result == 0,
4865 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4866 /* Simulates redo (Ctrl-Y) */
4867 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4868 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4869 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4870 result = strcmp(buffer,"cut\r\n");
4871 todo_wine ok(result == 0,
4872 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4873 release_key(VK_CONTROL);
4875 DestroyWindow(hwndRichEdit);
4878 static void test_EM_FORMATRANGE(void)
4880 int r, i, tpp_x, tpp_y;
4881 HDC hdc;
4882 HWND hwndRichEdit = new_richedit(NULL);
4883 FORMATRANGE fr;
4884 BOOL skip_non_english;
4885 static const struct {
4886 const char *string; /* The string */
4887 int first; /* First 'pagebreak', 0 for don't care */
4888 int second; /* Second 'pagebreak', 0 for don't care */
4889 } fmtstrings[] = {
4890 {"WINE wine", 0, 0},
4891 {"WINE wineWine", 0, 0},
4892 {"WINE\r\nwine\r\nwine", 5, 10},
4893 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4894 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4897 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4898 if (skip_non_english)
4899 skip("Skipping some tests on non-English platform\n");
4901 hdc = GetDC(hwndRichEdit);
4902 ok(hdc != NULL, "Could not get HDC\n");
4904 /* Calculate the twips per pixel */
4905 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4906 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4908 /* Test the simple case where all the text fits in the page rect. */
4909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4910 fr.hdc = fr.hdcTarget = hdc;
4911 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4912 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4913 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4914 fr.chrg.cpMin = 0;
4915 fr.chrg.cpMax = -1;
4916 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4917 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4919 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4920 fr.rc.bottom = fr.rcPage.bottom;
4921 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4922 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4924 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4926 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4928 GETTEXTLENGTHEX gtl;
4929 SIZE stringsize;
4930 int len;
4932 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4934 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4935 gtl.codepage = CP_ACP;
4936 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4938 /* Get some size information for the string */
4939 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4941 /* Define the box to be half the width needed and a bit larger than the height.
4942 * Changes to the width means we have at least 2 pages. Changes to the height
4943 * is done so we can check the changing of fr.rc.bottom.
4945 fr.hdc = fr.hdcTarget = hdc;
4946 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4947 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4948 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4950 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4951 todo_wine {
4952 ok(r == len, "Expected %d, got %d\n", len, r);
4955 /* We know that the page can't hold the full string. See how many characters
4956 * are on the first one
4958 fr.chrg.cpMin = 0;
4959 fr.chrg.cpMax = -1;
4960 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4961 todo_wine {
4962 if (! skip_non_english)
4963 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4965 if (fmtstrings[i].first)
4966 todo_wine {
4967 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4969 else
4970 ok(r < len, "Expected < %d, got %d\n", len, r);
4972 /* Do another page */
4973 fr.chrg.cpMin = r;
4974 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4975 if (fmtstrings[i].second)
4976 todo_wine {
4977 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4979 else if (! skip_non_english)
4980 ok (r < len, "Expected < %d, got %d\n", len, r);
4982 /* There is at least on more page, but we don't care */
4984 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4985 todo_wine {
4986 ok(r == len, "Expected %d, got %d\n", len, r);
4990 ReleaseDC(NULL, hdc);
4991 DestroyWindow(hwndRichEdit);
4994 static int nCallbackCount = 0;
4996 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4997 LONG cb, LONG* pcb)
4999 const char text[] = {'t','e','s','t'};
5001 if (sizeof(text) <= cb)
5003 if ((int)dwCookie != nCallbackCount)
5005 *pcb = 0;
5006 return 0;
5009 memcpy (pbBuff, text, sizeof(text));
5010 *pcb = sizeof(text);
5012 nCallbackCount++;
5014 return 0;
5016 else
5017 return 1; /* indicates callback failed */
5020 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5021 LPBYTE pbBuff,
5022 LONG cb,
5023 LONG *pcb)
5025 const char** str = (const char**)dwCookie;
5026 int size = strlen(*str);
5027 *pcb = cb;
5028 if (*pcb > size) {
5029 *pcb = size;
5031 if (*pcb > 0) {
5032 memcpy(pbBuff, *str, *pcb);
5033 *str += *pcb;
5035 return 0;
5038 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5039 LPBYTE pbBuff,
5040 LONG cb,
5041 LONG *pcb)
5043 DWORD *phase = (DWORD *)dwCookie;
5045 if(*phase == 0){
5046 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5047 *pcb = sizeof(first) - 1;
5048 memcpy(pbBuff, first, *pcb);
5049 }else if(*phase == 1){
5050 static const char second[] = "\x8f\xc3\x8b";
5051 *pcb = sizeof(second) - 1;
5052 memcpy(pbBuff, second, *pcb);
5053 }else
5054 *pcb = 0;
5056 ++*phase;
5058 return 0;
5061 struct StringWithLength {
5062 int length;
5063 char *buffer;
5066 /* This callback is used to handled the null characters in a string. */
5067 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5068 LPBYTE pbBuff,
5069 LONG cb,
5070 LONG *pcb)
5072 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5073 int size = str->length;
5074 *pcb = cb;
5075 if (*pcb > size) {
5076 *pcb = size;
5078 if (*pcb > 0) {
5079 memcpy(pbBuff, str->buffer, *pcb);
5080 str->buffer += *pcb;
5081 str->length -= *pcb;
5083 return 0;
5086 static void test_EM_STREAMIN(void)
5088 HWND hwndRichEdit = new_richedit(NULL);
5089 DWORD phase;
5090 LRESULT result;
5091 EDITSTREAM es;
5092 char buffer[1024] = {0};
5094 const char * streamText0 = "{\\rtf1 TestSomeText}";
5095 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5096 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5098 const char * streamText1 =
5099 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5100 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5101 "}\r\n";
5103 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5104 const char * streamText2 =
5105 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5106 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5107 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5108 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5109 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5110 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5111 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5113 const char * streamText3 = "RichEdit1";
5115 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5117 const char * streamText4 =
5118 "This text just needs to be long enough to cause run to be split onto "
5119 "two separate lines and make sure the null terminating character is "
5120 "handled properly.\0";
5121 int length4 = strlen(streamText4) + 1;
5122 struct StringWithLength cookieForStream4 = {
5123 length4,
5124 (char *)streamText4,
5127 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5128 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5129 struct StringWithLength cookieForStream5 = {
5130 sizeof(streamText5),
5131 (char *)streamText5,
5134 /* Minimal test without \par at the end */
5135 es.dwCookie = (DWORD_PTR)&streamText0;
5136 es.dwError = 0;
5137 es.pfnCallback = test_EM_STREAMIN_esCallback;
5138 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5139 ok(result == 12, "got %ld, expected %d\n", result, 12);
5141 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5142 ok (result == 12,
5143 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5144 result = strcmp (buffer,"TestSomeText");
5145 ok (result == 0,
5146 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5147 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5149 /* Native richedit 2.0 ignores last \par */
5150 es.dwCookie = (DWORD_PTR)&streamText0a;
5151 es.dwError = 0;
5152 es.pfnCallback = test_EM_STREAMIN_esCallback;
5153 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5154 ok(result == 12, "got %ld, expected %d\n", result, 12);
5156 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5157 ok (result == 12,
5158 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5159 result = strcmp (buffer,"TestSomeText");
5160 ok (result == 0,
5161 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5162 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5164 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5165 es.dwCookie = (DWORD_PTR)&streamText0b;
5166 es.dwError = 0;
5167 es.pfnCallback = test_EM_STREAMIN_esCallback;
5168 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5169 ok(result == 13, "got %ld, expected %d\n", result, 13);
5171 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5172 ok (result == 14,
5173 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5174 result = strcmp (buffer,"TestSomeText\r\n");
5175 ok (result == 0,
5176 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5177 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5179 es.dwCookie = (DWORD_PTR)&streamText1;
5180 es.dwError = 0;
5181 es.pfnCallback = test_EM_STREAMIN_esCallback;
5182 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5183 ok(result == 12, "got %ld, expected %d\n", result, 12);
5185 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5186 ok (result == 12,
5187 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5188 result = strcmp (buffer,"TestSomeText");
5189 ok (result == 0,
5190 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5191 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5193 es.dwCookie = (DWORD_PTR)&streamText2;
5194 es.dwError = 0;
5195 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5196 ok(result == 0, "got %ld, expected %d\n", result, 0);
5198 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5199 ok (result == 0,
5200 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5201 ok (strlen(buffer) == 0,
5202 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5203 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5205 es.dwCookie = (DWORD_PTR)&streamText3;
5206 es.dwError = 0;
5207 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5208 ok(result == 0, "got %ld, expected %d\n", result, 0);
5210 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5211 ok (result == 0,
5212 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5213 ok (strlen(buffer) == 0,
5214 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5215 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5217 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5218 es.dwError = 0;
5219 es.pfnCallback = test_EM_STREAMIN_esCallback;
5220 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5221 ok(result == 18, "got %ld, expected %d\n", result, 18);
5223 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5224 ok(result == 15,
5225 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5226 result = strcmp (buffer,"TestUTF8WithBOM");
5227 ok(result == 0,
5228 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5229 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5231 phase = 0;
5232 es.dwCookie = (DWORD_PTR)&phase;
5233 es.dwError = 0;
5234 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5235 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5236 ok(result == 8, "got %ld\n", result);
5238 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5239 ok(result == 3,
5240 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5241 result = memcmp (buffer,"\xd6\xcf\xcb", 3);
5242 ok(result == 0,
5243 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5244 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5246 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5247 es.dwError = 0;
5248 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5249 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5250 ok(result == length4, "got %ld, expected %d\n", result, length4);
5252 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5253 ok (result == length4,
5254 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5255 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5257 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5258 es.dwError = 0;
5259 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5260 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5261 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5263 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5264 ok (result == length5,
5265 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5266 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5268 DestroyWindow(hwndRichEdit);
5271 static void test_EM_StreamIn_Undo(void)
5273 /* The purpose of this test is to determine when a EM_StreamIn should be
5274 * undoable. This is important because WM_PASTE currently uses StreamIn and
5275 * pasting should always be undoable but streaming isn't always.
5277 * cases to test:
5278 * StreamIn plain text without SFF_SELECTION.
5279 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5280 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5281 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5282 * Feel free to add tests for other text modes or StreamIn things.
5286 HWND hwndRichEdit = new_richedit(NULL);
5287 LRESULT result;
5288 EDITSTREAM es;
5289 char buffer[1024] = {0};
5290 const char randomtext[] = "Some text";
5292 es.pfnCallback = EditStreamCallback;
5294 /* StreamIn, no SFF_SELECTION */
5295 es.dwCookie = nCallbackCount;
5296 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5297 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5298 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5299 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5300 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5301 result = strcmp (buffer,"test");
5302 ok (result == 0,
5303 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5305 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5306 ok (result == FALSE,
5307 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5309 /* StreamIn, SFF_SELECTION, but nothing selected */
5310 es.dwCookie = nCallbackCount;
5311 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5312 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5313 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5314 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5315 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5316 result = strcmp (buffer,"testSome text");
5317 ok (result == 0,
5318 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5320 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5321 ok (result == TRUE,
5322 "EM_STREAMIN with SFF_SELECTION but no selection set "
5323 "should create an undo\n");
5325 /* StreamIn, SFF_SELECTION, with a selection */
5326 es.dwCookie = nCallbackCount;
5327 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5328 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5329 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5330 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5331 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5332 result = strcmp (buffer,"Sometesttext");
5333 ok (result == 0,
5334 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5336 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5337 ok (result == TRUE,
5338 "EM_STREAMIN with SFF_SELECTION and selection set "
5339 "should create an undo\n");
5341 DestroyWindow(hwndRichEdit);
5344 static BOOL is_em_settextex_supported(HWND hwnd)
5346 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5347 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5350 static void test_unicode_conversions(void)
5352 static const WCHAR tW[] = {'t',0};
5353 static const WCHAR teW[] = {'t','e',0};
5354 static const WCHAR textW[] = {'t','e','s','t',0};
5355 static const char textA[] = "test";
5356 char bufA[64];
5357 WCHAR bufW[64];
5358 HWND hwnd;
5359 int em_settextex_supported, ret;
5361 #define set_textA(hwnd, wm_set_text, txt) \
5362 do { \
5363 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5364 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5365 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5366 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5367 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5368 } while(0)
5369 #define expect_textA(hwnd, wm_get_text, txt) \
5370 do { \
5371 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5372 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5373 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5374 memset(bufA, 0xAA, sizeof(bufA)); \
5375 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5376 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5377 ret = lstrcmpA(bufA, txt); \
5378 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5379 } while(0)
5381 #define set_textW(hwnd, wm_set_text, txt) \
5382 do { \
5383 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5384 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5385 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5386 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5387 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5388 } while(0)
5389 #define expect_textW(hwnd, wm_get_text, txt) \
5390 do { \
5391 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5392 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5393 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5394 memset(bufW, 0xAA, sizeof(bufW)); \
5395 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5396 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5397 ret = lstrcmpW(bufW, txt); \
5398 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5399 } while(0)
5400 #define expect_empty(hwnd, wm_get_text) \
5401 do { \
5402 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5403 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5404 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5405 memset(bufA, 0xAA, sizeof(bufA)); \
5406 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5407 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5408 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5409 } while(0)
5411 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5412 0, 0, 200, 60, 0, 0, 0, 0);
5413 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5415 ret = IsWindowUnicode(hwnd);
5416 ok(ret, "RichEdit20W should be unicode under NT\n");
5418 /* EM_SETTEXTEX is supported starting from version 3.0 */
5419 em_settextex_supported = is_em_settextex_supported(hwnd);
5420 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5421 em_settextex_supported ? "" : "NOT ");
5423 expect_empty(hwnd, WM_GETTEXT);
5424 expect_empty(hwnd, EM_GETTEXTEX);
5426 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5427 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5428 expect_textA(hwnd, WM_GETTEXT, "t");
5429 expect_textA(hwnd, EM_GETTEXTEX, "t");
5430 expect_textW(hwnd, EM_GETTEXTEX, tW);
5432 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5433 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5434 expect_textA(hwnd, WM_GETTEXT, "te");
5435 expect_textA(hwnd, EM_GETTEXTEX, "te");
5436 expect_textW(hwnd, EM_GETTEXTEX, teW);
5438 set_textA(hwnd, WM_SETTEXT, NULL);
5439 expect_empty(hwnd, WM_GETTEXT);
5440 expect_empty(hwnd, EM_GETTEXTEX);
5442 set_textA(hwnd, WM_SETTEXT, textA);
5443 expect_textA(hwnd, WM_GETTEXT, textA);
5444 expect_textA(hwnd, EM_GETTEXTEX, textA);
5445 expect_textW(hwnd, EM_GETTEXTEX, textW);
5447 if (em_settextex_supported)
5449 set_textA(hwnd, EM_SETTEXTEX, textA);
5450 expect_textA(hwnd, WM_GETTEXT, textA);
5451 expect_textA(hwnd, EM_GETTEXTEX, textA);
5452 expect_textW(hwnd, EM_GETTEXTEX, textW);
5455 set_textW(hwnd, WM_SETTEXT, textW);
5456 expect_textW(hwnd, WM_GETTEXT, textW);
5457 expect_textA(hwnd, WM_GETTEXT, textA);
5458 expect_textW(hwnd, EM_GETTEXTEX, textW);
5459 expect_textA(hwnd, EM_GETTEXTEX, textA);
5461 if (em_settextex_supported)
5463 set_textW(hwnd, EM_SETTEXTEX, textW);
5464 expect_textW(hwnd, WM_GETTEXT, textW);
5465 expect_textA(hwnd, WM_GETTEXT, textA);
5466 expect_textW(hwnd, EM_GETTEXTEX, textW);
5467 expect_textA(hwnd, EM_GETTEXTEX, textA);
5469 DestroyWindow(hwnd);
5471 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5472 0, 0, 200, 60, 0, 0, 0, 0);
5473 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5475 ret = IsWindowUnicode(hwnd);
5476 ok(!ret, "RichEdit20A should NOT be unicode\n");
5478 set_textA(hwnd, WM_SETTEXT, textA);
5479 expect_textA(hwnd, WM_GETTEXT, textA);
5480 expect_textA(hwnd, EM_GETTEXTEX, textA);
5481 expect_textW(hwnd, EM_GETTEXTEX, textW);
5483 if (em_settextex_supported)
5485 set_textA(hwnd, EM_SETTEXTEX, textA);
5486 expect_textA(hwnd, WM_GETTEXT, textA);
5487 expect_textA(hwnd, EM_GETTEXTEX, textA);
5488 expect_textW(hwnd, EM_GETTEXTEX, textW);
5491 set_textW(hwnd, WM_SETTEXT, textW);
5492 expect_textW(hwnd, WM_GETTEXT, textW);
5493 expect_textA(hwnd, WM_GETTEXT, textA);
5494 expect_textW(hwnd, EM_GETTEXTEX, textW);
5495 expect_textA(hwnd, EM_GETTEXTEX, textA);
5497 if (em_settextex_supported)
5499 set_textW(hwnd, EM_SETTEXTEX, textW);
5500 expect_textW(hwnd, WM_GETTEXT, textW);
5501 expect_textA(hwnd, WM_GETTEXT, textA);
5502 expect_textW(hwnd, EM_GETTEXTEX, textW);
5503 expect_textA(hwnd, EM_GETTEXTEX, textA);
5505 DestroyWindow(hwnd);
5508 static void test_WM_CHAR(void)
5510 HWND hwnd;
5511 int ret;
5512 const char * char_list = "abc\rabc\r";
5513 const char * expected_content_single = "abcabc";
5514 const char * expected_content_multi = "abc\r\nabc\r\n";
5515 char buffer[64] = {0};
5516 const char * p;
5518 /* single-line control must IGNORE carriage returns */
5519 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5520 0, 0, 200, 60, 0, 0, 0, 0);
5521 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5523 p = char_list;
5524 while (*p != '\0') {
5525 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5526 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5527 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5528 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5529 p++;
5532 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5533 ret = strcmp(buffer, expected_content_single);
5534 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5536 DestroyWindow(hwnd);
5538 /* multi-line control inserts CR normally */
5539 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5540 0, 0, 200, 60, 0, 0, 0, 0);
5541 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5543 p = char_list;
5544 while (*p != '\0') {
5545 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5546 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5547 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5548 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5549 p++;
5552 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5553 ret = strcmp(buffer, expected_content_multi);
5554 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5556 DestroyWindow(hwnd);
5559 static void test_EM_GETTEXTLENGTHEX(void)
5561 HWND hwnd;
5562 GETTEXTLENGTHEX gtl;
5563 int ret;
5564 const char * base_string = "base string";
5565 const char * test_string = "a\nb\n\n\r\n";
5566 const char * test_string_after = "a";
5567 const char * test_string_2 = "a\rtest\rstring";
5568 char buffer[64] = {0};
5570 /* single line */
5571 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5572 0, 0, 200, 60, 0, 0, 0, 0);
5573 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5575 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5576 gtl.codepage = CP_ACP;
5577 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5578 ok(ret == 0, "ret %d\n",ret);
5580 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5581 gtl.codepage = CP_ACP;
5582 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5583 ok(ret == 0, "ret %d\n",ret);
5585 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5587 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5588 gtl.codepage = CP_ACP;
5589 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5590 ok(ret == strlen(base_string), "ret %d\n",ret);
5592 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5593 gtl.codepage = CP_ACP;
5594 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5595 ok(ret == strlen(base_string), "ret %d\n",ret);
5597 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5599 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5600 gtl.codepage = CP_ACP;
5601 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5602 ok(ret == 1, "ret %d\n",ret);
5604 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5605 gtl.codepage = CP_ACP;
5606 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5607 ok(ret == 1, "ret %d\n",ret);
5609 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5610 ret = strcmp(buffer, test_string_after);
5611 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5613 DestroyWindow(hwnd);
5615 /* multi line */
5616 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5617 0, 0, 200, 60, 0, 0, 0, 0);
5618 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5620 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5621 gtl.codepage = CP_ACP;
5622 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5623 ok(ret == 0, "ret %d\n",ret);
5625 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5626 gtl.codepage = CP_ACP;
5627 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5628 ok(ret == 0, "ret %d\n",ret);
5630 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5632 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5633 gtl.codepage = CP_ACP;
5634 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5635 ok(ret == strlen(base_string), "ret %d\n",ret);
5637 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5638 gtl.codepage = CP_ACP;
5639 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5640 ok(ret == strlen(base_string), "ret %d\n",ret);
5642 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5644 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5645 gtl.codepage = CP_ACP;
5646 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5647 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5649 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5650 gtl.codepage = CP_ACP;
5651 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5652 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5654 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5656 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5657 gtl.codepage = CP_ACP;
5658 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5659 ok(ret == 10, "ret %d\n",ret);
5661 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5662 gtl.codepage = CP_ACP;
5663 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5664 ok(ret == 6, "ret %d\n",ret);
5666 /* Unicode/NUMCHARS/NUMBYTES */
5667 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5669 gtl.flags = GTL_DEFAULT;
5670 gtl.codepage = 1200;
5671 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5672 ok(ret == lstrlen(test_string_2),
5673 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5675 gtl.flags = GTL_NUMCHARS;
5676 gtl.codepage = 1200;
5677 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5678 ok(ret == lstrlen(test_string_2),
5679 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5681 gtl.flags = GTL_NUMBYTES;
5682 gtl.codepage = 1200;
5683 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5684 ok(ret == lstrlen(test_string_2)*2,
5685 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5687 gtl.flags = GTL_PRECISE;
5688 gtl.codepage = 1200;
5689 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5690 ok(ret == lstrlen(test_string_2)*2,
5691 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5693 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5694 gtl.codepage = 1200;
5695 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5696 ok(ret == lstrlen(test_string_2),
5697 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5699 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5700 gtl.codepage = 1200;
5701 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5702 ok(ret == E_INVALIDARG,
5703 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5705 DestroyWindow(hwnd);
5709 /* globals that parent and child access when checking event masks & notifications */
5710 static HWND eventMaskEditHwnd = 0;
5711 static int queriedEventMask;
5712 static int watchForEventMask = 0;
5714 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5715 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5717 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5719 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5721 return DefWindowProcA(hwnd, message, wParam, lParam);
5724 /* test event masks in combination with WM_COMMAND */
5725 static void test_eventMask(void)
5727 HWND parent;
5728 int ret, style;
5729 WNDCLASSA cls;
5730 const char text[] = "foo bar\n";
5731 int eventMask;
5733 /* register class to capture WM_COMMAND */
5734 cls.style = 0;
5735 cls.lpfnWndProc = ParentMsgCheckProcA;
5736 cls.cbClsExtra = 0;
5737 cls.cbWndExtra = 0;
5738 cls.hInstance = GetModuleHandleA(0);
5739 cls.hIcon = 0;
5740 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5741 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5742 cls.lpszMenuName = NULL;
5743 cls.lpszClassName = "EventMaskParentClass";
5744 if(!RegisterClassA(&cls)) assert(0);
5746 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5747 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5748 ok (parent != 0, "Failed to create parent window\n");
5750 eventMaskEditHwnd = new_richedit(parent);
5751 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5753 eventMask = ENM_CHANGE | ENM_UPDATE;
5754 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5755 ok(ret == ENM_NONE, "wrong event mask\n");
5756 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5757 ok(ret == eventMask, "failed to set event mask\n");
5759 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5760 queriedEventMask = 0; /* initialize to something other than we expect */
5761 watchForEventMask = EN_CHANGE;
5762 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5763 ok(ret == TRUE, "failed to set text\n");
5764 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5765 notification in response to WM_SETTEXT */
5766 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5767 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5769 /* check to see if EN_CHANGE is sent when redraw is turned off */
5770 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5771 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5772 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5773 /* redraw is disabled by making the window invisible. */
5774 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5775 queriedEventMask = 0; /* initialize to something other than we expect */
5776 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5777 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5778 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5779 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5780 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5782 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5783 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5784 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5785 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5786 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5787 watchForEventMask = EN_UPDATE;
5788 queriedEventMask = 0; /* initialize to something other than we expect */
5789 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5790 ok(queriedEventMask == 0,
5791 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5792 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5793 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5794 queriedEventMask = 0; /* initialize to something other than we expect */
5795 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5796 ok(queriedEventMask == eventMask,
5797 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5800 DestroyWindow(parent);
5803 static int received_WM_NOTIFY = 0;
5804 static int modify_at_WM_NOTIFY = 0;
5805 static BOOL filter_on_WM_NOTIFY = FALSE;
5806 static HWND hwndRichedit_WM_NOTIFY;
5808 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5810 if(message == WM_NOTIFY)
5812 received_WM_NOTIFY = 1;
5813 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5814 if (filter_on_WM_NOTIFY) return TRUE;
5816 return DefWindowProcA(hwnd, message, wParam, lParam);
5819 static void test_WM_NOTIFY(void)
5821 HWND parent;
5822 WNDCLASSA cls;
5823 CHARFORMAT2 cf2;
5824 int sel_start, sel_end;
5826 /* register class to capture WM_NOTIFY */
5827 cls.style = 0;
5828 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5829 cls.cbClsExtra = 0;
5830 cls.cbWndExtra = 0;
5831 cls.hInstance = GetModuleHandleA(0);
5832 cls.hIcon = 0;
5833 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5834 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5835 cls.lpszMenuName = NULL;
5836 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5837 if(!RegisterClassA(&cls)) assert(0);
5839 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5840 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5841 ok (parent != 0, "Failed to create parent window\n");
5843 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5844 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5846 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5848 /* Notifications for selection change should only be sent when selection
5849 actually changes. EM_SETCHARFORMAT is one message that calls
5850 ME_CommitUndo, which should check whether message should be sent */
5851 received_WM_NOTIFY = 0;
5852 cf2.cbSize = sizeof(CHARFORMAT2);
5853 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5854 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5855 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5856 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5857 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5859 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5860 already at 0. */
5861 received_WM_NOTIFY = 0;
5862 modify_at_WM_NOTIFY = 0;
5863 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5864 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5865 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5867 received_WM_NOTIFY = 0;
5868 modify_at_WM_NOTIFY = 0;
5869 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5870 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5872 received_WM_NOTIFY = 0;
5873 modify_at_WM_NOTIFY = 0;
5874 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5875 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5876 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5878 /* Test for WM_NOTIFY messages with redraw disabled. */
5879 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5880 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5881 received_WM_NOTIFY = 0;
5882 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5883 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5884 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5886 /* Test filtering key events. */
5887 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5888 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5889 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5890 received_WM_NOTIFY = 0;
5891 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5892 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5893 ok(sel_start == 1 && sel_end == 1,
5894 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5895 filter_on_WM_NOTIFY = TRUE;
5896 received_WM_NOTIFY = 0;
5897 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5898 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5899 ok(sel_start == 1 && sel_end == 1,
5900 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5902 /* test with owner set to NULL */
5903 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5904 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5905 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5906 ok(sel_start == 1 && sel_end == 1,
5907 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5909 DestroyWindow(hwndRichedit_WM_NOTIFY);
5910 DestroyWindow(parent);
5913 static void test_undo_coalescing(void)
5915 HWND hwnd;
5916 int result;
5917 char buffer[64] = {0};
5919 /* multi-line control inserts CR normally */
5920 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5921 0, 0, 200, 60, 0, 0, 0, 0);
5922 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5924 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5925 ok (result == FALSE, "Can undo after window creation.\n");
5926 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5927 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5928 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5929 ok (result == FALSE, "Can redo after window creation.\n");
5930 result = SendMessage(hwnd, EM_REDO, 0, 0);
5931 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5933 /* Test the effect of arrows keys during typing on undo transactions*/
5934 simulate_typing_characters(hwnd, "one two three");
5935 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5936 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5937 simulate_typing_characters(hwnd, " four five six");
5939 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5940 ok (result == FALSE, "Can redo before anything is undone.\n");
5941 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5942 ok (result == TRUE, "Cannot undo typed characters.\n");
5943 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5944 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5945 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5946 ok (result == TRUE, "Cannot redo after undo.\n");
5947 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5948 result = strcmp(buffer, "one two three");
5949 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5951 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5952 ok (result == TRUE, "Cannot undo typed characters.\n");
5953 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5954 ok (result == TRUE, "Failed to undo typed characters.\n");
5955 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5956 result = strcmp(buffer, "");
5957 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5959 /* Test the effect of focus changes during typing on undo transactions*/
5960 simulate_typing_characters(hwnd, "one two three");
5961 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5962 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5963 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5964 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5965 simulate_typing_characters(hwnd, " four five six");
5966 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5967 ok (result == TRUE, "Failed to undo typed characters.\n");
5968 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5969 result = strcmp(buffer, "one two three");
5970 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5972 /* Test the effect of the back key during typing on undo transactions */
5973 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5974 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5975 ok (result == TRUE, "Failed to clear the text.\n");
5976 simulate_typing_characters(hwnd, "one two threa");
5977 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5978 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5979 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5980 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5981 simulate_typing_characters(hwnd, "e four five six");
5982 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5983 ok (result == TRUE, "Failed to undo typed characters.\n");
5984 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5985 result = strcmp(buffer, "");
5986 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5988 /* Test the effect of the delete key during typing on undo transactions */
5989 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5990 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5991 ok(result == TRUE, "Failed to set the text.\n");
5992 SendMessage(hwnd, EM_SETSEL, 1, 1);
5993 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5994 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5995 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5996 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5997 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5998 ok (result == TRUE, "Failed to undo typed characters.\n");
5999 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6000 result = strcmp(buffer, "acd");
6001 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6002 result = SendMessage(hwnd, EM_UNDO, 0, 0);
6003 ok (result == TRUE, "Failed to undo typed characters.\n");
6004 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6005 result = strcmp(buffer, "abcd");
6006 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6008 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6009 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6010 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6011 ok (result == TRUE, "Failed to clear the text.\n");
6012 simulate_typing_characters(hwnd, "one two three");
6013 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
6014 ok (result == 0, "expected %d but got %d\n", 0, result);
6015 simulate_typing_characters(hwnd, " four five six");
6016 result = SendMessage(hwnd, EM_UNDO, 0, 0);
6017 ok (result == TRUE, "Failed to undo typed characters.\n");
6018 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6019 result = strcmp(buffer, "one two three");
6020 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6021 result = SendMessage(hwnd, EM_UNDO, 0, 0);
6022 ok (result == TRUE, "Failed to undo typed characters.\n");
6023 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6024 result = strcmp(buffer, "");
6025 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6027 DestroyWindow(hwnd);
6030 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6032 int length;
6034 /* MSDN lied, length is actually the number of bytes. */
6035 length = bytes / sizeof(WCHAR);
6036 switch(code)
6038 case WB_ISDELIMITER:
6039 return text[pos] == 'X';
6040 case WB_LEFT:
6041 case WB_MOVEWORDLEFT:
6042 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6043 return pos-1;
6044 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6045 case WB_LEFTBREAK:
6046 pos--;
6047 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6048 pos--;
6049 return pos;
6050 case WB_RIGHT:
6051 case WB_MOVEWORDRIGHT:
6052 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6053 return pos+1;
6054 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6055 case WB_RIGHTBREAK:
6056 pos++;
6057 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6058 pos++;
6059 return pos;
6060 default:
6061 ok(FALSE, "Unexpected code %d\n", code);
6062 break;
6064 return 0;
6067 static void test_word_movement(void)
6069 HWND hwnd;
6070 int result;
6071 int sel_start, sel_end;
6072 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6074 /* multi-line control inserts CR normally */
6075 hwnd = new_richedit(NULL);
6077 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6078 ok (result == TRUE, "Failed to clear the text.\n");
6079 SendMessage(hwnd, EM_SETSEL, 0, 0);
6080 /* |one two three */
6082 send_ctrl_key(hwnd, VK_RIGHT);
6083 /* one |two three */
6084 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6085 ok(sel_start == sel_end, "Selection should be empty\n");
6086 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6088 send_ctrl_key(hwnd, VK_RIGHT);
6089 /* one two |three */
6090 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6091 ok(sel_start == sel_end, "Selection should be empty\n");
6092 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6094 send_ctrl_key(hwnd, VK_LEFT);
6095 /* one |two three */
6096 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6097 ok(sel_start == sel_end, "Selection should be empty\n");
6098 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6100 send_ctrl_key(hwnd, VK_LEFT);
6101 /* |one two three */
6102 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6103 ok(sel_start == sel_end, "Selection should be empty\n");
6104 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6106 SendMessage(hwnd, EM_SETSEL, 8, 8);
6107 /* one two | three */
6108 send_ctrl_key(hwnd, VK_RIGHT);
6109 /* one two |three */
6110 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6111 ok(sel_start == sel_end, "Selection should be empty\n");
6112 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6114 SendMessage(hwnd, EM_SETSEL, 11, 11);
6115 /* one two th|ree */
6116 send_ctrl_key(hwnd, VK_LEFT);
6117 /* one two |three */
6118 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6119 ok(sel_start == sel_end, "Selection should be empty\n");
6120 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6122 /* Test with a custom word break procedure that uses X as the delimiter. */
6123 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6124 ok (result == TRUE, "Failed to clear the text.\n");
6125 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6126 /* |one twoXthree */
6127 send_ctrl_key(hwnd, VK_RIGHT);
6128 /* one twoX|three */
6129 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6130 ok(sel_start == sel_end, "Selection should be empty\n");
6131 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6133 DestroyWindow(hwnd);
6135 /* Make sure the behaviour is the same with a unicode richedit window,
6136 * and using unicode functions. */
6138 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6139 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6140 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6142 /* Test with a custom word break procedure that uses X as the delimiter. */
6143 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6144 ok (result == TRUE, "Failed to clear the text.\n");
6145 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6146 /* |one twoXthree */
6147 send_ctrl_key(hwnd, VK_RIGHT);
6148 /* one twoX|three */
6149 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6150 ok(sel_start == sel_end, "Selection should be empty\n");
6151 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6153 DestroyWindow(hwnd);
6156 static void test_EM_CHARFROMPOS(void)
6158 HWND hwnd;
6159 int result;
6160 RECT rcClient;
6161 POINTL point;
6162 point.x = 0;
6163 point.y = 40;
6165 /* multi-line control inserts CR normally */
6166 hwnd = new_richedit(NULL);
6167 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6168 (LPARAM)"one two three four five six seven\reight");
6169 ok(result == 1, "Expected 1, got %d\n", result);
6170 GetClientRect(hwnd, &rcClient);
6172 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6173 ok(result == 34, "expected character index of 34 but got %d\n", result);
6175 /* Test with points outside the bounds of the richedit control. */
6176 point.x = -1;
6177 point.y = 40;
6178 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6179 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6181 point.x = 1000;
6182 point.y = 0;
6183 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6184 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6186 point.x = 1000;
6187 point.y = 36;
6188 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6189 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6191 point.x = 1000;
6192 point.y = -1;
6193 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6194 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6196 point.x = 1000;
6197 point.y = rcClient.bottom + 1;
6198 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6199 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6201 point.x = 1000;
6202 point.y = rcClient.bottom;
6203 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6204 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6206 DestroyWindow(hwnd);
6209 static void test_word_wrap(void)
6211 HWND hwnd;
6212 POINTL point = {0, 60}; /* This point must be below the first line */
6213 const char *text = "Must be long enough to test line wrapping";
6214 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6215 int res, pos, lines;
6217 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6218 * when specified on window creation and set later. */
6219 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6220 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6221 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6222 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6223 ok(res, "WM_SETTEXT failed.\n");
6224 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6225 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6226 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6227 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6229 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6230 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6231 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6232 DestroyWindow(hwnd);
6234 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6235 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6236 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6238 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6239 ok(res, "WM_SETTEXT failed.\n");
6240 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6241 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6242 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6243 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6245 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6246 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6247 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6248 DestroyWindow(hwnd);
6250 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6251 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6252 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6253 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6254 ok(res, "WM_SETTEXT failed.\n");
6255 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6256 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6258 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6259 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6260 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6261 DestroyWindow(hwnd);
6263 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6264 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6265 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6266 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6267 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6268 ok(res, "WM_SETTEXT failed.\n");
6269 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6270 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6272 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6273 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6274 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6276 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6277 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6278 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6279 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6280 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6282 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6283 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6284 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6285 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6286 DestroyWindow(hwnd);
6288 /* Test to see if wrapping happens with redraw disabled. */
6289 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6290 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6291 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6292 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6293 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6294 ok(res, "EM_REPLACESEL failed.\n");
6295 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6296 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6297 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6298 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6299 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6301 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6302 DestroyWindow(hwnd);
6305 static void test_autoscroll(void)
6307 HWND hwnd = new_richedit(NULL);
6308 int lines, ret, redraw;
6309 POINT pt;
6311 for (redraw = 0; redraw <= 1; redraw++) {
6312 trace("testing with WM_SETREDRAW=%d\n", redraw);
6313 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6314 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6315 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6316 ok(lines == 8, "%d lines instead of 8\n", lines);
6317 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6318 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6319 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6320 ret = GetWindowLong(hwnd, GWL_STYLE);
6321 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6323 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6324 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6325 ok(lines == 1, "%d lines instead of 1\n", lines);
6326 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6327 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6328 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6329 ret = GetWindowLong(hwnd, GWL_STYLE);
6330 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6333 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6334 DestroyWindow(hwnd);
6336 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6337 * auto vertical/horizontal scrolling options. */
6338 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6339 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6340 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6341 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6342 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6343 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6344 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6345 ret = GetWindowLong(hwnd, GWL_STYLE);
6346 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6347 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6348 DestroyWindow(hwnd);
6350 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6351 WS_POPUP|ES_MULTILINE,
6352 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6353 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6354 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6355 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6356 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6357 ret = GetWindowLong(hwnd, GWL_STYLE);
6358 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6359 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6360 DestroyWindow(hwnd);
6364 static void test_format_rect(void)
6366 HWND hwnd;
6367 RECT rc, expected, clientRect;
6368 int n;
6369 DWORD options;
6371 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6372 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6373 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6374 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6376 GetClientRect(hwnd, &clientRect);
6378 expected = clientRect;
6379 expected.left += 1;
6380 expected.right -= 1;
6381 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6382 ok(rc.top == expected.top && rc.left == expected.left &&
6383 rc.bottom == expected.bottom && rc.right == expected.right,
6384 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6385 rc.top, rc.left, rc.bottom, rc.right,
6386 expected.top, expected.left, expected.bottom, expected.right);
6388 for (n = -3; n <= 3; n++)
6390 rc = clientRect;
6391 rc.top += n;
6392 rc.left += n;
6393 rc.bottom -= n;
6394 rc.right -= n;
6395 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6397 expected = rc;
6398 expected.top = max(0, rc.top);
6399 expected.left = max(0, rc.left);
6400 expected.bottom = min(clientRect.bottom, rc.bottom);
6401 expected.right = min(clientRect.right, rc.right);
6402 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6403 ok(rc.top == expected.top && rc.left == expected.left &&
6404 rc.bottom == expected.bottom && rc.right == expected.right,
6405 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6406 n, rc.top, rc.left, rc.bottom, rc.right,
6407 expected.top, expected.left, expected.bottom, expected.right);
6410 rc = clientRect;
6411 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6412 expected = clientRect;
6413 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6414 ok(rc.top == expected.top && rc.left == expected.left &&
6415 rc.bottom == expected.bottom && rc.right == expected.right,
6416 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6417 rc.top, rc.left, rc.bottom, rc.right,
6418 expected.top, expected.left, expected.bottom, expected.right);
6420 /* Adding the selectionbar adds the selectionbar width to the left side. */
6421 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6422 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6423 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6424 expected.left += 8; /* selection bar width */
6425 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6426 ok(rc.top == expected.top && rc.left == expected.left &&
6427 rc.bottom == expected.bottom && rc.right == expected.right,
6428 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6429 rc.top, rc.left, rc.bottom, rc.right,
6430 expected.top, expected.left, expected.bottom, expected.right);
6432 rc = clientRect;
6433 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6434 expected = clientRect;
6435 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6436 ok(rc.top == expected.top && rc.left == expected.left &&
6437 rc.bottom == expected.bottom && rc.right == expected.right,
6438 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6439 rc.top, rc.left, rc.bottom, rc.right,
6440 expected.top, expected.left, expected.bottom, expected.right);
6442 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6443 * even if the left side is already 0. */
6444 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6445 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6446 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6447 expected.left -= 8; /* selection bar width */
6448 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6449 ok(rc.top == expected.top && rc.left == expected.left &&
6450 rc.bottom == expected.bottom && rc.right == expected.right,
6451 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6452 rc.top, rc.left, rc.bottom, rc.right,
6453 expected.top, expected.left, expected.bottom, expected.right);
6455 /* Set the absolute value of the formatting rectangle. */
6456 rc = clientRect;
6457 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6458 expected = clientRect;
6459 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6460 ok(rc.top == expected.top && rc.left == expected.left &&
6461 rc.bottom == expected.bottom && rc.right == expected.right,
6462 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6463 n, rc.top, rc.left, rc.bottom, rc.right,
6464 expected.top, expected.left, expected.bottom, expected.right);
6466 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6467 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6468 * tests show that this isn't true. */
6469 rc.top = 15;
6470 rc.left = 15;
6471 rc.bottom = clientRect.bottom - 15;
6472 rc.right = clientRect.right - 15;
6473 expected = rc;
6474 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6475 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6476 ok(rc.top == expected.top && rc.left == expected.left &&
6477 rc.bottom == expected.bottom && rc.right == expected.right,
6478 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6479 rc.top, rc.left, rc.bottom, rc.right,
6480 expected.top, expected.left, expected.bottom, expected.right);
6482 /* For some reason it does not limit the values to the client rect with
6483 * a WPARAM value of 1. */
6484 rc.top = -15;
6485 rc.left = -15;
6486 rc.bottom = clientRect.bottom + 15;
6487 rc.right = clientRect.right + 15;
6488 expected = rc;
6489 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6490 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6491 ok(rc.top == expected.top && rc.left == expected.left &&
6492 rc.bottom == expected.bottom && rc.right == expected.right,
6493 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6494 rc.top, rc.left, rc.bottom, rc.right,
6495 expected.top, expected.left, expected.bottom, expected.right);
6497 /* Reset to default rect and check how the format rect adjusts to window
6498 * resize and how it copes with very small windows */
6499 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6501 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6502 GetClientRect(hwnd, &clientRect);
6504 expected = clientRect;
6505 expected.left += 1;
6506 expected.right -= 1;
6507 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6508 ok(rc.top == expected.top && rc.left == expected.left &&
6509 rc.bottom == expected.bottom && rc.right == expected.right,
6510 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6511 rc.top, rc.left, rc.bottom, rc.right,
6512 expected.top, expected.left, expected.bottom, expected.right);
6514 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6515 GetClientRect(hwnd, &clientRect);
6517 expected = clientRect;
6518 expected.left += 1;
6519 expected.right -= 1;
6520 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6521 ok(rc.top == expected.top && rc.left == expected.left &&
6522 rc.bottom == expected.bottom && rc.right == expected.right,
6523 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6524 rc.top, rc.left, rc.bottom, rc.right,
6525 expected.top, expected.left, expected.bottom, expected.right);
6527 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6528 GetClientRect(hwnd, &clientRect);
6530 expected = clientRect;
6531 expected.left += 1;
6532 expected.right -= 1;
6533 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6534 ok(rc.top == expected.top && rc.left == expected.left &&
6535 rc.bottom == expected.bottom && rc.right == expected.right,
6536 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6537 rc.top, rc.left, rc.bottom, rc.right,
6538 expected.top, expected.left, expected.bottom, expected.right);
6540 DestroyWindow(hwnd);
6542 /* The extended window style affects the formatting rectangle. */
6543 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6544 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6545 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6546 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6548 GetClientRect(hwnd, &clientRect);
6550 expected = clientRect;
6551 expected.left += 1;
6552 expected.top += 1;
6553 expected.right -= 1;
6554 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6555 ok(rc.top == expected.top && rc.left == expected.left &&
6556 rc.bottom == expected.bottom && rc.right == expected.right,
6557 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6558 rc.top, rc.left, rc.bottom, rc.right,
6559 expected.top, expected.left, expected.bottom, expected.right);
6561 rc = clientRect;
6562 rc.top += 5;
6563 rc.left += 5;
6564 rc.bottom -= 5;
6565 rc.right -= 5;
6566 expected = rc;
6567 expected.top -= 1;
6568 expected.left -= 1;
6569 expected.right += 1;
6570 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6571 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6572 ok(rc.top == expected.top && rc.left == expected.left &&
6573 rc.bottom == expected.bottom && rc.right == expected.right,
6574 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6575 rc.top, rc.left, rc.bottom, rc.right,
6576 expected.top, expected.left, expected.bottom, expected.right);
6578 DestroyWindow(hwnd);
6581 static void test_WM_GETDLGCODE(void)
6583 HWND hwnd;
6584 UINT res, expected;
6585 MSG msg;
6587 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6589 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6590 ES_MULTILINE|ES_WANTRETURN|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, 0);
6595 expected = expected | DLGC_WANTMESSAGE;
6596 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6597 res, expected);
6598 DestroyWindow(hwnd);
6600 msg.message = WM_KEYDOWN;
6601 msg.wParam = VK_RETURN;
6602 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6603 msg.pt.x = 0;
6604 msg.pt.y = 0;
6605 msg.time = GetTickCount();
6607 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6608 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6609 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6610 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6611 msg.hwnd = hwnd;
6612 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6613 expected = expected | DLGC_WANTMESSAGE;
6614 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6615 res, expected);
6616 DestroyWindow(hwnd);
6618 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6619 ES_MULTILINE|WS_POPUP,
6620 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6621 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6622 msg.hwnd = hwnd;
6623 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6624 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6625 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6626 res, expected);
6627 DestroyWindow(hwnd);
6629 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6630 ES_WANTRETURN|WS_POPUP,
6631 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6632 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6633 msg.hwnd = hwnd;
6634 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6635 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6636 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6637 res, expected);
6638 DestroyWindow(hwnd);
6640 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6641 WS_POPUP,
6642 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6643 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6644 msg.hwnd = hwnd;
6645 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6646 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6647 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6648 res, expected);
6649 DestroyWindow(hwnd);
6651 msg.wParam = VK_TAB;
6652 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6654 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6655 ES_MULTILINE|WS_POPUP,
6656 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6657 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6658 msg.hwnd = hwnd;
6659 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6660 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6661 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6662 res, expected);
6663 DestroyWindow(hwnd);
6665 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6666 WS_POPUP,
6667 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6668 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6669 msg.hwnd = hwnd;
6670 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6671 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6672 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6673 res, expected);
6674 DestroyWindow(hwnd);
6676 hold_key(VK_CONTROL);
6678 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6679 ES_MULTILINE|WS_POPUP,
6680 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6681 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6682 msg.hwnd = hwnd;
6683 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6684 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6685 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6686 res, expected);
6687 DestroyWindow(hwnd);
6689 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6690 WS_POPUP,
6691 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6692 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6693 msg.hwnd = hwnd;
6694 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6695 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6696 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6697 res, expected);
6698 DestroyWindow(hwnd);
6700 release_key(VK_CONTROL);
6702 msg.wParam = 'a';
6703 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6705 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6706 ES_MULTILINE|WS_POPUP,
6707 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6708 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6709 msg.hwnd = hwnd;
6710 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6711 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6712 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6713 res, expected);
6714 DestroyWindow(hwnd);
6716 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6717 WS_POPUP,
6718 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6719 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6720 msg.hwnd = hwnd;
6721 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6722 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6723 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6724 res, expected);
6725 DestroyWindow(hwnd);
6727 msg.message = WM_CHAR;
6729 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6730 ES_MULTILINE|WS_POPUP,
6731 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6732 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6733 msg.hwnd = hwnd;
6734 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6735 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6736 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6737 res, expected);
6738 DestroyWindow(hwnd);
6740 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6741 WS_POPUP,
6742 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6743 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6744 msg.hwnd = hwnd;
6745 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6746 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6747 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6748 res, expected);
6749 DestroyWindow(hwnd);
6752 static void test_zoom(void)
6754 HWND hwnd;
6755 UINT ret;
6756 RECT rc;
6757 POINT pt;
6758 int numerator, denominator;
6760 hwnd = new_richedit(NULL);
6761 GetClientRect(hwnd, &rc);
6762 pt.x = (rc.right - rc.left) / 2;
6763 pt.y = (rc.bottom - rc.top) / 2;
6764 ClientToScreen(hwnd, &pt);
6766 /* Test initial zoom value */
6767 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6768 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6769 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6770 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6772 /* test scroll wheel */
6773 hold_key(VK_CONTROL);
6774 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6775 MAKELPARAM(pt.x, pt.y));
6776 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6777 release_key(VK_CONTROL);
6779 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6780 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6781 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6782 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6784 /* Test how much the mouse wheel can zoom in and out. */
6785 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6786 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6788 hold_key(VK_CONTROL);
6789 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6790 MAKELPARAM(pt.x, pt.y));
6791 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6792 release_key(VK_CONTROL);
6794 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6795 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6796 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6797 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6799 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6800 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6802 hold_key(VK_CONTROL);
6803 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6804 MAKELPARAM(pt.x, pt.y));
6805 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6806 release_key(VK_CONTROL);
6808 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6809 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6810 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6811 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6813 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6814 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6816 hold_key(VK_CONTROL);
6817 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6818 MAKELPARAM(pt.x, pt.y));
6819 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6820 release_key(VK_CONTROL);
6822 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6823 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6824 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6825 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6827 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6828 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6830 hold_key(VK_CONTROL);
6831 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6832 MAKELPARAM(pt.x, pt.y));
6833 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6834 release_key(VK_CONTROL);
6836 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6837 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6838 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6839 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6841 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6842 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6843 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6845 hold_key(VK_CONTROL);
6846 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6847 MAKELPARAM(pt.x, pt.y));
6848 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6849 release_key(VK_CONTROL);
6851 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6852 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6853 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6854 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6856 /* Test bounds checking on EM_SETZOOM */
6857 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6858 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6860 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6861 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6863 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6864 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6866 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6867 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6868 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6869 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6871 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6872 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6874 /* See if negative numbers are accepted. */
6875 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6876 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6878 /* See if negative numbers are accepted. */
6879 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6880 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6882 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6883 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6884 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6885 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6887 /* Reset the zoom value */
6888 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6889 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6891 DestroyWindow(hwnd);
6894 struct dialog_mode_messages
6896 int wm_getdefid, wm_close, wm_nextdlgctl;
6899 static struct dialog_mode_messages dm_messages;
6901 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6902 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6903 "got %d\n", wmclose, dm_messages.wm_close); \
6904 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6905 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6906 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6907 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6909 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6911 switch (iMsg)
6913 case DM_GETDEFID:
6914 dm_messages.wm_getdefid++;
6915 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6916 case WM_NEXTDLGCTL:
6917 dm_messages.wm_nextdlgctl++;
6918 break;
6919 case WM_CLOSE:
6920 dm_messages.wm_close++;
6921 break;
6924 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6927 static void test_dialogmode(void)
6929 HWND hwRichEdit, hwParent, hwButton;
6930 MSG msg= {0};
6931 int lcount, r;
6932 WNDCLASSA cls;
6934 cls.style = 0;
6935 cls.lpfnWndProc = dialog_mode_wnd_proc;
6936 cls.cbClsExtra = 0;
6937 cls.cbWndExtra = 0;
6938 cls.hInstance = GetModuleHandleA(0);
6939 cls.hIcon = 0;
6940 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6941 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6942 cls.lpszMenuName = NULL;
6943 cls.lpszClassName = "DialogModeParentClass";
6944 if(!RegisterClassA(&cls)) assert(0);
6946 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6947 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6949 /* Test richedit(ES_MULTILINE) */
6951 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6953 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6954 ok(0 == r, "expected 0, got %d\n", r);
6955 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6956 ok(2 == lcount, "expected 2, got %d\n", lcount);
6958 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6959 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6961 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6962 ok(0 == r, "expected 0, got %d\n", r);
6963 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6964 ok(3 == lcount, "expected 3, got %d\n", lcount);
6966 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6967 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6968 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6969 ok(0 == r, "expected 0, got %d\n", r);
6970 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6971 ok(3 == lcount, "expected 3, got %d\n", lcount);
6973 DestroyWindow(hwRichEdit);
6975 /* Test standalone richedit(ES_MULTILINE) */
6977 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6979 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6980 ok(0 == r, "expected 0, got %d\n", r);
6981 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6982 ok(2 == lcount, "expected 2, got %d\n", lcount);
6984 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6985 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6987 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6988 ok(0 == r, "expected 0, got %d\n", r);
6989 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6990 ok(2 == lcount, "expected 2, got %d\n", lcount);
6992 DestroyWindow(hwRichEdit);
6994 /* Check a destination for messages */
6996 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6998 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6999 SetParent( hwRichEdit, NULL);
7001 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7002 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7004 memset(&dm_messages, 0, sizeof(dm_messages));
7005 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7006 ok(0 == r, "expected 0, got %d\n", r);
7007 test_dm_messages(0, 1, 0);
7009 memset(&dm_messages, 0, sizeof(dm_messages));
7010 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7011 ok(0 == r, "expected 0, got %d\n", r);
7012 test_dm_messages(0, 0, 1);
7014 DestroyWindow(hwRichEdit);
7016 /* Check messages from richedit(ES_MULTILINE) */
7018 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
7020 memset(&dm_messages, 0, sizeof(dm_messages));
7021 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7022 ok(0 == r, "expected 0, got %d\n", r);
7023 test_dm_messages(0, 0, 0);
7025 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7026 ok(2 == lcount, "expected 2, got %d\n", lcount);
7028 memset(&dm_messages, 0, sizeof(dm_messages));
7029 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7030 ok(0 == r, "expected 0, got %d\n", r);
7031 test_dm_messages(0, 0, 0);
7033 memset(&dm_messages, 0, sizeof(dm_messages));
7034 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7035 ok(0 == r, "expected 0, got %d\n", r);
7036 test_dm_messages(0, 0, 0);
7038 memset(&dm_messages, 0, sizeof(dm_messages));
7039 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7040 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7041 test_dm_messages(0, 0, 0);
7043 memset(&dm_messages, 0, sizeof(dm_messages));
7044 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7045 ok(0 == r, "expected 0, got %d\n", r);
7046 test_dm_messages(0, 1, 0);
7048 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7049 ok(2 == lcount, "expected 2, got %d\n", lcount);
7051 memset(&dm_messages, 0, sizeof(dm_messages));
7052 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7053 ok(0 == r, "expected 0, got %d\n", r);
7054 test_dm_messages(0, 0, 0);
7056 memset(&dm_messages, 0, sizeof(dm_messages));
7057 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7058 ok(0 == r, "expected 0, got %d\n", r);
7059 test_dm_messages(0, 0, 1);
7061 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7062 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7063 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7065 memset(&dm_messages, 0, sizeof(dm_messages));
7066 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7067 ok(0 == r, "expected 0, got %d\n", r);
7068 test_dm_messages(0, 1, 1);
7070 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7071 ok(2 == lcount, "expected 2, got %d\n", lcount);
7073 DestroyWindow(hwButton);
7074 DestroyWindow(hwRichEdit);
7076 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7078 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
7080 memset(&dm_messages, 0, sizeof(dm_messages));
7081 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7082 ok(0 == r, "expected 0, got %d\n", r);
7083 test_dm_messages(0, 0, 0);
7085 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7086 ok(2 == lcount, "expected 2, got %d\n", lcount);
7088 memset(&dm_messages, 0, sizeof(dm_messages));
7089 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7090 ok(0 == r, "expected 0, got %d\n", r);
7091 test_dm_messages(0, 0, 0);
7093 memset(&dm_messages, 0, sizeof(dm_messages));
7094 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7095 ok(0 == r, "expected 0, got %d\n", r);
7096 test_dm_messages(0, 0, 0);
7098 memset(&dm_messages, 0, sizeof(dm_messages));
7099 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7100 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7101 test_dm_messages(0, 0, 0);
7103 memset(&dm_messages, 0, sizeof(dm_messages));
7104 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7105 ok(0 == r, "expected 0, got %d\n", r);
7106 test_dm_messages(0, 0, 0);
7108 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7109 ok(3 == lcount, "expected 3, got %d\n", lcount);
7111 memset(&dm_messages, 0, sizeof(dm_messages));
7112 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7113 ok(0 == r, "expected 0, got %d\n", r);
7114 test_dm_messages(0, 0, 0);
7116 memset(&dm_messages, 0, sizeof(dm_messages));
7117 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7118 ok(0 == r, "expected 0, got %d\n", r);
7119 test_dm_messages(0, 0, 1);
7121 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7122 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7123 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7125 memset(&dm_messages, 0, sizeof(dm_messages));
7126 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7127 ok(0 == r, "expected 0, got %d\n", r);
7128 test_dm_messages(0, 0, 0);
7130 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7131 ok(4 == lcount, "expected 4, got %d\n", lcount);
7133 DestroyWindow(hwButton);
7134 DestroyWindow(hwRichEdit);
7136 /* Check messages from richedit(0) */
7138 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7140 memset(&dm_messages, 0, sizeof(dm_messages));
7141 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7142 ok(0 == r, "expected 0, got %d\n", r);
7143 test_dm_messages(0, 0, 0);
7145 memset(&dm_messages, 0, sizeof(dm_messages));
7146 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7147 ok(0 == r, "expected 0, got %d\n", r);
7148 test_dm_messages(0, 0, 0);
7150 memset(&dm_messages, 0, sizeof(dm_messages));
7151 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7152 ok(0 == r, "expected 0, got %d\n", r);
7153 test_dm_messages(0, 0, 0);
7155 memset(&dm_messages, 0, sizeof(dm_messages));
7156 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7157 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7158 test_dm_messages(0, 0, 0);
7160 memset(&dm_messages, 0, sizeof(dm_messages));
7161 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7162 ok(0 == r, "expected 0, got %d\n", r);
7163 test_dm_messages(0, 1, 0);
7165 memset(&dm_messages, 0, sizeof(dm_messages));
7166 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7167 ok(0 == r, "expected 0, got %d\n", r);
7168 test_dm_messages(0, 0, 0);
7170 memset(&dm_messages, 0, sizeof(dm_messages));
7171 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7172 ok(0 == r, "expected 0, got %d\n", r);
7173 test_dm_messages(0, 0, 1);
7175 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7176 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7177 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7179 memset(&dm_messages, 0, sizeof(dm_messages));
7180 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7181 ok(0 == r, "expected 0, got %d\n", r);
7182 test_dm_messages(0, 1, 1);
7184 DestroyWindow(hwRichEdit);
7186 /* Check messages from richedit(ES_WANTRETURN) */
7188 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7190 memset(&dm_messages, 0, sizeof(dm_messages));
7191 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7192 ok(0 == r, "expected 0, got %d\n", r);
7193 test_dm_messages(0, 0, 0);
7195 memset(&dm_messages, 0, sizeof(dm_messages));
7196 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7197 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7198 test_dm_messages(0, 0, 0);
7200 memset(&dm_messages, 0, sizeof(dm_messages));
7201 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7202 ok(0 == r, "expected 0, got %d\n", r);
7203 test_dm_messages(0, 0, 0);
7205 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7206 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7207 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7209 memset(&dm_messages, 0, sizeof(dm_messages));
7210 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7211 ok(0 == r, "expected 0, got %d\n", r);
7212 test_dm_messages(0, 0, 0);
7214 DestroyWindow(hwRichEdit);
7215 DestroyWindow(hwParent);
7218 static void test_EM_FINDWORDBREAK_W(void)
7220 static const struct {
7221 WCHAR c;
7222 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7223 } delimiter_tests[] = {
7224 {0x0a, FALSE}, /* newline */
7225 {0x0b, FALSE}, /* vertical tab */
7226 {0x0c, FALSE}, /* form feed */
7227 {0x0d, FALSE}, /* carriage return */
7228 {0x20, TRUE}, /* space */
7229 {0x61, FALSE}, /* capital letter a */
7230 {0xa0, FALSE}, /* no-break space */
7231 {0x2000, FALSE}, /* en quad */
7232 {0x3000, FALSE}, /* Ideographic space */
7233 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7234 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7235 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7236 {0xac00, FALSE}, /* Hangul character GA*/
7237 {0xd7af, FALSE}, /* End of Hangul character chart */
7238 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7239 {0xff20, FALSE}, /* fullwidth commercial @ */
7240 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7242 int i;
7243 HWND hwndRichEdit = new_richeditW(NULL);
7244 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7245 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7247 WCHAR wbuf[2];
7248 int result;
7250 wbuf[0] = delimiter_tests[i].c;
7251 wbuf[1] = 0;
7252 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7253 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7254 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7255 todo_wine
7256 ok(result == delimiter_tests[i].isdelimiter,
7257 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7258 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7259 else
7260 ok(result == delimiter_tests[i].isdelimiter,
7261 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7262 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7264 DestroyWindow(hwndRichEdit);
7267 static void test_EM_FINDWORDBREAK_A(void)
7269 static const struct {
7270 WCHAR c;
7271 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7272 } delimiter_tests[] = {
7273 {0x0a, FALSE}, /* newline */
7274 {0x0b, FALSE}, /* vertical tab */
7275 {0x0c, FALSE}, /* form feed */
7276 {0x0d, FALSE}, /* carriage return */
7277 {0x20, TRUE}, /* space */
7278 {0x61, FALSE}, /* capital letter a */
7280 int i;
7281 HWND hwndRichEdit = new_richedit(NULL);
7283 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7284 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7286 int result;
7287 char buf[2];
7288 buf[0] = delimiter_tests[i].c;
7289 buf[1] = 0;
7290 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7291 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7292 if (buf[0] == 0x20)
7293 todo_wine
7294 ok(result == delimiter_tests[i].isdelimiter,
7295 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7296 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7297 else
7298 ok(result == delimiter_tests[i].isdelimiter,
7299 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7300 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7302 DestroyWindow(hwndRichEdit);
7306 * This test attempts to show the effect of enter on a richedit
7307 * control v1.0 inserts CRLF whereas for higher versions it only
7308 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7309 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7310 * does for higher. The same test is cloned in riched32 and riched20.
7312 static void test_enter(void)
7314 static const struct {
7315 const char *initialtext;
7316 const int cursor;
7317 const char *expectedwmtext;
7318 const char *expectedemtext;
7319 const char *expectedemtextcrlf;
7320 } testenteritems[] = {
7321 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7322 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7323 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7324 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7325 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7328 char expectedbuf[1024];
7329 char resultbuf[1024];
7330 HWND hwndRichEdit = new_richedit(NULL);
7331 UINT i,j;
7333 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7335 char buf[1024] = {0};
7336 LRESULT result;
7337 GETTEXTEX getText;
7338 const char *expected;
7340 /* Set the text to the initial text */
7341 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
7342 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7344 /* Send Enter */
7345 SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7346 simulate_typing_characters(hwndRichEdit, "\r");
7348 /* 1. Retrieve with WM_GETTEXT */
7349 buf[0] = 0x00;
7350 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
7351 expected = testenteritems[i].expectedwmtext;
7353 resultbuf[0]=0x00;
7354 for (j = 0; j < (UINT)result; j++)
7355 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7356 expectedbuf[0] = '\0';
7357 for (j = 0; j < strlen(expected); j++)
7358 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7360 result = strcmp(expected, buf);
7361 ok (result == 0,
7362 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7363 i, resultbuf, expectedbuf);
7365 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7366 getText.cb = sizeof(buf);
7367 getText.flags = GT_DEFAULT;
7368 getText.codepage = CP_ACP;
7369 getText.lpDefaultChar = NULL;
7370 getText.lpUsedDefChar = NULL;
7371 buf[0] = 0x00;
7372 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7373 expected = testenteritems[i].expectedemtext;
7375 resultbuf[0]=0x00;
7376 for (j = 0; j < (UINT)result; j++)
7377 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7378 expectedbuf[0] = '\0';
7379 for (j = 0; j < strlen(expected); j++)
7380 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7382 result = strcmp(expected, buf);
7383 ok (result == 0,
7384 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7385 i, resultbuf, expectedbuf);
7387 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7388 getText.cb = sizeof(buf);
7389 getText.flags = GT_USECRLF;
7390 getText.codepage = CP_ACP;
7391 getText.lpDefaultChar = NULL;
7392 getText.lpUsedDefChar = NULL;
7393 buf[0] = 0x00;
7394 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7395 expected = testenteritems[i].expectedemtextcrlf;
7397 resultbuf[0]=0x00;
7398 for (j = 0; j < (UINT)result; j++)
7399 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7400 expectedbuf[0] = '\0';
7401 for (j = 0; j < strlen(expected); j++)
7402 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7404 result = strcmp(expected, buf);
7405 ok (result == 0,
7406 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7407 i, resultbuf, expectedbuf);
7410 DestroyWindow(hwndRichEdit);
7413 static void test_WM_CREATE(void)
7415 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7416 static const char title[] = "line1\nline2";
7418 HWND rich_edit;
7419 LRESULT res;
7420 char buf[64];
7421 int len;
7423 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7424 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7425 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7427 len = GetWindowText(rich_edit, buf, sizeof(buf));
7428 ok(len == 5, "GetWindowText returned %d\n", len);
7429 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7431 res = SendMessage(rich_edit, EM_GETSEL, 0, 0);
7432 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7434 DestroyWindow(rich_edit);
7436 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7437 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7438 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7440 len = GetWindowText(rich_edit, buf, sizeof(buf));
7441 ok(len == 12, "GetWindowText returned %d\n", len);
7442 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7444 res = SendMessage(rich_edit, EM_GETSEL, 0, 0);
7445 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7447 DestroyWindow(rich_edit);
7450 START_TEST( editor )
7452 BOOL ret;
7453 /* Must explicitly LoadLibrary(). The test has no references to functions in
7454 * RICHED20.DLL, so the linker doesn't actually link to it. */
7455 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7456 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7458 test_WM_CHAR();
7459 test_EM_FINDTEXT(FALSE);
7460 test_EM_FINDTEXT(TRUE);
7461 test_EM_GETLINE();
7462 test_EM_POSFROMCHAR();
7463 test_EM_SCROLLCARET();
7464 test_EM_SCROLL();
7465 test_scrollbar_visibility();
7466 test_WM_SETTEXT();
7467 test_EM_LINELENGTH();
7468 test_EM_SETCHARFORMAT();
7469 test_EM_SETTEXTMODE();
7470 test_TM_PLAINTEXT();
7471 test_EM_SETOPTIONS();
7472 test_WM_GETTEXT();
7473 test_EM_GETTEXTRANGE();
7474 test_EM_GETSELTEXT();
7475 test_EM_SETUNDOLIMIT();
7476 test_ES_PASSWORD();
7477 test_EM_SETTEXTEX();
7478 test_EM_LIMITTEXT();
7479 test_EM_EXLIMITTEXT();
7480 test_EM_GETLIMITTEXT();
7481 test_WM_SETFONT();
7482 test_EM_GETMODIFY();
7483 test_EM_EXSETSEL();
7484 test_WM_PASTE();
7485 test_EM_STREAMIN();
7486 test_EM_STREAMOUT();
7487 test_EM_STREAMOUT_FONTTBL();
7488 test_EM_StreamIn_Undo();
7489 test_EM_FORMATRANGE();
7490 test_unicode_conversions();
7491 test_EM_GETTEXTLENGTHEX();
7492 test_EM_REPLACESEL(1);
7493 test_EM_REPLACESEL(0);
7494 test_WM_NOTIFY();
7495 test_EM_AUTOURLDETECT();
7496 test_eventMask();
7497 test_undo_coalescing();
7498 test_word_movement();
7499 test_EM_CHARFROMPOS();
7500 test_SETPARAFORMAT();
7501 test_word_wrap();
7502 test_autoscroll();
7503 test_format_rect();
7504 test_WM_GETDLGCODE();
7505 test_zoom();
7506 test_dialogmode();
7507 test_EM_FINDWORDBREAK_W();
7508 test_EM_FINDWORDBREAK_A();
7509 test_enter();
7510 test_WM_CREATE();
7512 /* Set the environment variable WINETEST_RICHED20 to keep windows
7513 * responsive and open for 30 seconds. This is useful for debugging.
7515 if (getenv( "WINETEST_RICHED20" )) {
7516 keep_responsive(30);
7519 OleFlushClipboard();
7520 ret = FreeLibrary(hmoduleRichEdit);
7521 ok(ret, "error: %d\n", (int) GetLastError());