[BRANCHES]
[reactos.git] / rostests / winetests / riched20 / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
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.
12 *
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.
17 *
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
21 */
22
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>
35
36 #define ID_RICHEDITTESTDBUTTON 0x123
37
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
39
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);
46
47 static HMODULE hmoduleRichEdit;
48
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;
56 }
57
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;
65 }
66
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
69 }
70
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
73 }
74
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)
78 {
79 MSG msg;
80 time_t end;
81
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 (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessageA(&msg);
89 } else {
90 Sleep(50);
91 }
92 }
93 }
94
95 static void simulate_typing_characters(HWND hwnd, const char* szChars)
96 {
97 int ret;
98
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++;
105 }
106 }
107
108 static BOOL hold_key(int vk)
109 {
110 BYTE key_state[256];
111 BOOL result;
112
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;
120 }
121
122 static BOOL release_key(int vk)
123 {
124 BYTE key_state[256];
125 BOOL result;
126
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;
134 }
135
136 static const char haystack[] = "WINEWine wineWine wine WineWine";
137 /* ^0 ^10 ^20 ^30 */
138
139 struct find_s {
140 int start;
141 int end;
142 const char *needle;
143 int flags;
144 int expected_loc;
145 };
146
147
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}
155 };
156
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},
161
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},
167
168 /* Find backwards */
169 {19, 20, "Wine", FR_MATCHCASE, 13},
170 {10, 20, "Wine", FR_MATCHCASE, 4},
171 {20, 10, "Wine", FR_MATCHCASE, 13},
172
173 /* Case-insensitive */
174 {1, 31, "wInE", FR_DOWN, 4},
175 {1, 31, "Wine", FR_DOWN, 4},
176
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},
182
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},
188
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},
197
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},
204
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},
211
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},
216
217 {0, -1, "wineWine wine", 0, -1},
218 };
219
220 static WCHAR *atowstr(const char *str)
221 {
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;
228 }
229
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
231 {
232 int findloc;
233
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);
240
241 findloc = SendMessageA(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);
245
246 findloc = SendMessageA(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);
250
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;
258
259 findloc = SendMessageA(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);
263 }
264 }
265
266 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
267 int id, BOOL unicode)
268 {
269 int findloc;
270 int expected_end_loc;
271
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 = SendMessageA(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 = SendMessageA(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);
309 }
310 }
311
312 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
313 int num_tests, BOOL unicode)
314 {
315 int i;
316
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);
320 }
321 }
322
323 static void test_EM_FINDTEXT(BOOL unicode)
324 {
325 HWND hwndRichEdit;
326 CHARFORMAT2A cf2;
327
328 if(unicode)
329 hwndRichEdit = new_richeditW(NULL);
330 else
331 hwndRichEdit = new_richedit(NULL);
332
333 /* Empty rich edit control */
334 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
335 sizeof(find_tests)/sizeof(struct find_s), unicode);
336
337 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
338
339 /* Haystack text */
340 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
341 sizeof(find_tests2)/sizeof(struct find_s), unicode);
342
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(CHARFORMAT2A);
346 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
347 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
348 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
349 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
350 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
351
352 /* Haystack text, again */
353 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
354 sizeof(find_tests2)/sizeof(struct find_s), unicode);
355
356 /* Yet another range */
357 cf2.dwMask = CFM_BOLD | cf2.dwMask;
358 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
359 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
360 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
361
362 /* Haystack text, again */
363 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
364 sizeof(find_tests2)/sizeof(struct find_s), unicode);
365
366 DestroyWindow(hwndRichEdit);
367 }
368
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"},
378
379 /* Buffer smaller than line length */
380 {0, 2, "foo bar\r"},
381 {0, 1, "foo bar\r"},
382 {0, 0, "foo bar\r"}
383 };
384
385 static void test_EM_GETLINE(void)
386 {
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";
394
395 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
396
397 memset(origdest, 0xBB, nBuf);
398 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
399 {
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;
405
406 /* EM_GETLINE appends a "\r\0" to the end of the line
407 * nCopied counts up to and including the '\r' */
408 nCopied = SendMessageA(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
419 {
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);
434
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.
438 *
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);
451 }
452 }
453
454 DestroyWindow(hwndRichEdit);
455 }
456
457 static void test_EM_LINELENGTH(void)
458 {
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},
476 };
477 int i;
478 LRESULT result;
479
480 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
481
482 for (i = 0; i < 10; i++) {
483 result = SendMessageA(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]);
486 }
487
488 DestroyWindow(hwndRichEdit);
489 }
490
491 static int get_scroll_pos_y(HWND hwnd)
492 {
493 POINT p = {-1, -1};
494 SendMessageA(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;
497 }
498
499 static void move_cursor(HWND hwnd, LONG charindex)
500 {
501 CHARRANGE cr;
502 cr.cpMax = charindex;
503 cr.cpMin = charindex;
504 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
505 }
506
507 static void line_scroll(HWND hwnd, int amount)
508 {
509 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
510 }
511
512 static void test_EM_SCROLLCARET(void)
513 {
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.
527 */
528 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, 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_CLASS20A, (int) GetLastError());
532
533 /* Can't verify this */
534 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
535
536 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
537
538 /* Caret above visible window */
539 line_scroll(hwndRichEdit, 3);
540 prevY = get_scroll_pos_y(hwndRichEdit);
541 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
542 curY = get_scroll_pos_y(hwndRichEdit);
543 ok(prevY != curY, "%d == %d\n", prevY, curY);
544
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 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
550 curY = get_scroll_pos_y(hwndRichEdit);
551 ok(prevY != curY, "%d == %d\n", prevY, curY);
552
553 /* Caret in visible window */
554 move_cursor(hwndRichEdit, sizeof(text) - 2);
555 prevY = get_scroll_pos_y(hwndRichEdit);
556 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
557 curY = get_scroll_pos_y(hwndRichEdit);
558 ok(prevY == curY, "%d != %d\n", prevY, curY);
559
560 /* Caret still in visible window */
561 line_scroll(hwndRichEdit, -1);
562 prevY = get_scroll_pos_y(hwndRichEdit);
563 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
564 curY = get_scroll_pos_y(hwndRichEdit);
565 ok(prevY == curY, "%d != %d\n", prevY, curY);
566
567 DestroyWindow(hwndRichEdit);
568 }
569
570 static void test_EM_POSFROMCHAR(void)
571 {
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";
589
590 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
591 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
592 (sig.lsUsb[3] & 0x08000000) != 0);
593
594 /* Fill the control to lines to ensure that most of them are offscreen */
595 for (i = 0; i < 50; i++)
596 {
597 /* Do not modify the string; it is exactly 16 characters long. */
598 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
599 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
600 }
601
602 /*
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.
606 */
607
608 /* Testing Richedit 2.0 API format */
609
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?
613 */
614 for (i = 0; i < 50; i++)
615 {
616 /* All the lines are 16 characters long */
617 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
618 if (i == 0)
619 {
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);
623 }
624 else if (i == 1)
625 {
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);
629 }
630 else
631 {
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));
634 }
635 }
636
637 /* Testing position at end of text */
638 result = SendMessageA(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));
641
642 /* Testing position way past end of text */
643 result = SendMessageA(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);
647
648 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
649 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
650 for (i = 0; i < 50; i++)
651 {
652 /* All the lines are 16 characters long */
653 result = SendMessageA(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));
658 }
659
660 /* Testing position at end of text */
661 result = SendMessageA(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));
664
665 /* Testing position way past end of text */
666 result = SendMessageA(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);
670
671 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
672 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
673 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
674
675 result = SendMessageA(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);
679
680 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
681 result = SendMessageA(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 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
687
688 /* Test around end of text that doesn't end in a newline. */
689 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
690 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
691 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
692 ok(pt.x > 1, "pt.x = %d\n", pt.x);
693 xpos = pt.x;
694 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
695 SendMessageA(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 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
699 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
700 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
701
702 /* Try a negative position. */
703 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
704 ok(pt.x == 1, "pt.x = %d\n", pt.x);
705
706 DestroyWindow(hwndRichEdit);
707 }
708
709 static void test_EM_SETCHARFORMAT(void)
710 {
711 HWND hwndRichEdit = new_richedit(NULL);
712 CHARFORMAT2A 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,
723 0
724 };
725 int i;
726 CHARRANGE cr;
727 LOCALESIGNATURE sig;
728 BOOL rtl;
729
730 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
731 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
732 (sig.lsUsb[3] & 0x08000000) != 0);
733
734 /* Invalid flags, CHARFORMAT2 structure blanked out */
735 memset(&cf2, 0, sizeof(cf2));
736 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
737 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
738
739 /* A valid flag, CHARFORMAT2 structure blanked out */
740 memset(&cf2, 0, sizeof(cf2));
741 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
742 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
743
744 /* A valid flag, CHARFORMAT2 structure blanked out */
745 memset(&cf2, 0, sizeof(cf2));
746 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
747 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
748
749 /* A valid flag, CHARFORMAT2 structure blanked out */
750 memset(&cf2, 0, sizeof(cf2));
751 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
752 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
753
754 /* A valid flag, CHARFORMAT2 structure blanked out */
755 memset(&cf2, 0, sizeof(cf2));
756 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
757 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
758
759 /* Invalid flags, CHARFORMAT2 structure minimally filled */
760 memset(&cf2, 0, sizeof(cf2));
761 cf2.cbSize = sizeof(CHARFORMAT2A);
762 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
763 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
764 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
765 ok(rc == FALSE, "Should not be able to undo here.\n");
766 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
767
768 /* A valid flag, CHARFORMAT2 structure minimally filled */
769 memset(&cf2, 0, sizeof(cf2));
770 cf2.cbSize = sizeof(CHARFORMAT2A);
771 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
772 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
773 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
774 ok(rc == FALSE, "Should not be able to undo here.\n");
775 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
776
777 /* A valid flag, CHARFORMAT2 structure minimally filled */
778 memset(&cf2, 0, sizeof(cf2));
779 cf2.cbSize = sizeof(CHARFORMAT2A);
780 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
781 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
782 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
783 ok(rc == FALSE, "Should not be able to undo here.\n");
784 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
785
786 /* A valid flag, CHARFORMAT2 structure minimally filled */
787 memset(&cf2, 0, sizeof(cf2));
788 cf2.cbSize = sizeof(CHARFORMAT2A);
789 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
790 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
791 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
792 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
793 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
794
795 /* A valid flag, CHARFORMAT2 structure minimally filled */
796 memset(&cf2, 0, sizeof(cf2));
797 cf2.cbSize = sizeof(CHARFORMAT2A);
798 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
799 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
800 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
801 ok(rc == TRUE, "Should not be able to undo here.\n");
802 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
803
804 cf2.cbSize = sizeof(CHARFORMAT2A);
805 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
806
807 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
808 cf2.cbSize = sizeof(CHARFORMAT2A);
809 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
810 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
811 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
812
813 /* wParam==0 is default char format, does not set modify */
814 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
815 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
816 ok(rc == 0, "Text marked as modified, expected not modified!\n");
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
818 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
819 if (! rtl)
820 {
821 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
822 ok(rc == 0, "Text marked as modified, expected not modified!\n");
823 }
824 else
825 skip("RTL language found\n");
826
827 /* wParam==SCF_SELECTION sets modify if nonempty selection */
828 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
829 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
830 ok(rc == 0, "Text marked as modified, expected not modified!\n");
831 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
832 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
833 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
834 ok(rc == 0, "Text marked as modified, expected not modified!\n");
835
836 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
837 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
838 ok(rc == 0, "Text marked as modified, expected not modified!\n");
839 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
840 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
841 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
842 ok(rc == 0, "Text marked as modified, expected not modified!\n");
843 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
844 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
845 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
846 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
847 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
848
849 /* wParam==SCF_ALL sets modify regardless of whether text is present */
850 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
851 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
852 ok(rc == 0, "Text marked as modified, expected not modified!\n");
853 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
854 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
855 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
856 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
857
858 DestroyWindow(hwndRichEdit);
859
860 /* EM_GETCHARFORMAT tests */
861 for (i = 0; tested_effects[i]; i++)
862 {
863 hwndRichEdit = new_richedit(NULL);
864 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
865
866 /* Need to set a TrueType font to get consistent CFM_BOLD results */
867 memset(&cf2, 0, sizeof(CHARFORMAT2A));
868 cf2.cbSize = sizeof(CHARFORMAT2A);
869 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
870 cf2.dwEffects = 0;
871 strcpy(cf2.szFaceName, "Courier New");
872 cf2.wWeight = FW_DONTCARE;
873 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
874
875 memset(&cf2, 0, sizeof(CHARFORMAT2A));
876 cf2.cbSize = sizeof(CHARFORMAT2A);
877 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
878 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
879 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
880 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
881 ||
882 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
883 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
884 ok((cf2.dwEffects & tested_effects[i]) == 0,
885 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
886
887 memset(&cf2, 0, sizeof(CHARFORMAT2A));
888 cf2.cbSize = sizeof(CHARFORMAT2A);
889 cf2.dwMask = tested_effects[i];
890 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
891 cf2.dwMask = CFM_SUPERSCRIPT;
892 cf2.dwEffects = tested_effects[i];
893 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
894 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
895
896 memset(&cf2, 0, sizeof(CHARFORMAT2A));
897 cf2.cbSize = sizeof(CHARFORMAT2A);
898 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
899 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
900 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
901 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
902 ||
903 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
904 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
905 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
906 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
907
908 memset(&cf2, 0, sizeof(CHARFORMAT2A));
909 cf2.cbSize = sizeof(CHARFORMAT2A);
910 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
911 SendMessageA(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)
914 ||
915 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
916 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
917 ok((cf2.dwEffects & tested_effects[i]) == 0,
918 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
919
920 memset(&cf2, 0, sizeof(CHARFORMAT2A));
921 cf2.cbSize = sizeof(CHARFORMAT2A);
922 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
923 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
924 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
926 ||
927 (cf2.dwMask & tested_effects[i]) == 0),
928 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
929
930 DestroyWindow(hwndRichEdit);
931 }
932
933 for (i = 0; tested_effects[i]; i++)
934 {
935 hwndRichEdit = new_richedit(NULL);
936 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
937
938 /* Need to set a TrueType font to get consistent CFM_BOLD results */
939 memset(&cf2, 0, sizeof(CHARFORMAT2A));
940 cf2.cbSize = sizeof(CHARFORMAT2A);
941 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
942 cf2.dwEffects = 0;
943 strcpy(cf2.szFaceName, "Courier New");
944 cf2.wWeight = FW_DONTCARE;
945 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
946
947 memset(&cf2, 0, sizeof(CHARFORMAT2A));
948 cf2.cbSize = sizeof(CHARFORMAT2A);
949 cf2.dwMask = tested_effects[i];
950 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
951 cf2.dwMask = CFM_SUPERSCRIPT;
952 cf2.dwEffects = tested_effects[i];
953 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
954 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
955
956 memset(&cf2, 0, sizeof(CHARFORMAT2A));
957 cf2.cbSize = sizeof(CHARFORMAT2A);
958 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
959 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
960 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
961 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
962 ||
963 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
964 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
965 ok((cf2.dwEffects & tested_effects[i]) == 0,
966 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
967
968 memset(&cf2, 0, sizeof(CHARFORMAT2A));
969 cf2.cbSize = sizeof(CHARFORMAT2A);
970 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
971 SendMessageA(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)
974 ||
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]) == tested_effects[i],
978 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
979
980 memset(&cf2, 0, sizeof(CHARFORMAT2A));
981 cf2.cbSize = sizeof(CHARFORMAT2A);
982 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
983 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
984 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
986 ||
987 (cf2.dwMask & tested_effects[i]) == 0),
988 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\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 set\n", i, cf2.dwEffects, tested_effects[i]);
991
992 DestroyWindow(hwndRichEdit);
993 }
994
995 /* Effects applied on an empty selection should take effect when selection is
996 replaced with text */
997 hwndRichEdit = new_richedit(NULL);
998 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
999 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1000
1001 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1002 cf2.cbSize = sizeof(CHARFORMAT2A);
1003 cf2.dwMask = CFM_BOLD;
1004 cf2.dwEffects = CFE_BOLD;
1005 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1006
1007 /* Selection is now nonempty */
1008 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1009
1010 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1011 cf2.cbSize = sizeof(CHARFORMAT2A);
1012 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1013 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1014
1015 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1016 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1017 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1018 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1019
1020
1021 /* Set two effects on an empty selection */
1022 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1024
1025 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1026 cf2.cbSize = sizeof(CHARFORMAT2A);
1027 cf2.dwMask = CFM_BOLD;
1028 cf2.dwEffects = CFE_BOLD;
1029 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1030 cf2.dwMask = CFM_ITALIC;
1031 cf2.dwEffects = CFE_ITALIC;
1032 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1033
1034 /* Selection is now nonempty */
1035 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1036
1037 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1038 cf2.cbSize = sizeof(CHARFORMAT2A);
1039 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1040 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1041
1042 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1043 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1044 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1045 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1046
1047 /* Setting the (empty) selection to exactly the same place as before should
1048 NOT clear the insertion style! */
1049 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1050 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1051
1052 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1053 cf2.cbSize = sizeof(CHARFORMAT2A);
1054 cf2.dwMask = CFM_BOLD;
1055 cf2.dwEffects = CFE_BOLD;
1056 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1057
1058 /* Empty selection in same place, insert style should NOT be forgotten here. */
1059 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1060
1061 /* Selection is now nonempty */
1062 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1063
1064 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1065 cf2.cbSize = sizeof(CHARFORMAT2A);
1066 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1067 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1068
1069 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1070 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1071 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1072 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1073
1074 /* Ditto with EM_EXSETSEL */
1075 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1076 cr.cpMin = 2; cr.cpMax = 2;
1077 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1078
1079 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1080 cf2.cbSize = sizeof(CHARFORMAT2A);
1081 cf2.dwMask = CFM_BOLD;
1082 cf2.dwEffects = CFE_BOLD;
1083 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1084
1085 /* Empty selection in same place, insert style should NOT be forgotten here. */
1086 cr.cpMin = 2; cr.cpMax = 2;
1087 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1088
1089 /* Selection is now nonempty */
1090 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1091
1092 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1093 cf2.cbSize = sizeof(CHARFORMAT2A);
1094 cr.cpMin = 2; cr.cpMax = 6;
1095 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1096 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1097
1098 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1099 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1100 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1101 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1102
1103 DestroyWindow(hwndRichEdit);
1104 }
1105
1106 static void test_EM_SETTEXTMODE(void)
1107 {
1108 HWND hwndRichEdit = new_richedit(NULL);
1109 CHARFORMAT2A cf2, cf2test;
1110 CHARRANGE cr;
1111 int rc = 0;
1112
1113 /*Attempt to use mutually exclusive modes*/
1114 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1115 ok(rc == E_INVALIDARG,
1116 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1117
1118 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1119 /*Insert text into the control*/
1120
1121 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1122
1123 /*Attempt to change the control to plain text mode*/
1124 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1125 ok(rc == E_UNEXPECTED,
1126 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1127
1128 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1129 If rich text is pasted, it should have the same formatting as the rest
1130 of the text in the control*/
1131
1132 /*Italicize the text
1133 *NOTE: If the default text was already italicized, the test will simply
1134 reverse; in other words, it will copy a regular "wine" into a plain
1135 text window that uses an italicized format*/
1136 cf2.cbSize = sizeof(CHARFORMAT2A);
1137 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1138
1139 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1140 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1141
1142 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1143 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1144
1145 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1146 however, SCF_ALL has been implemented*/
1147 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1148 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1149
1150 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1151 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1152
1153 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1154
1155 /*Select the string "wine"*/
1156 cr.cpMin = 0;
1157 cr.cpMax = 4;
1158 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1159
1160 /*Copy the italicized "wine" to the clipboard*/
1161 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1162
1163 /*Reset the formatting to default*/
1164 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1165 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1166 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1167
1168 /*Clear the text in the control*/
1169 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1170
1171 /*Switch to Plain Text Mode*/
1172 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1173 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1174
1175 /*Input "wine" again in normal format*/
1176 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1177
1178 /*Paste the italicized "wine" into the control*/
1179 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1180
1181 /*Select a character from the first "wine" string*/
1182 cr.cpMin = 2;
1183 cr.cpMax = 3;
1184 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1185
1186 /*Retrieve its formatting*/
1187 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1188
1189 /*Select a character from the second "wine" string*/
1190 cr.cpMin = 5;
1191 cr.cpMax = 6;
1192 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1193
1194 /*Retrieve its formatting*/
1195 cf2test.cbSize = sizeof(CHARFORMAT2A);
1196 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1197
1198 /*Compare the two formattings*/
1199 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1200 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1201 cf2.dwEffects, cf2test.dwEffects);
1202 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1203 printing "wine" in the current format(normal)
1204 pasting "wine" from the clipboard(italicized)
1205 comparing the two formats(should differ)*/
1206
1207 /*Attempt to switch with text in control*/
1208 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1209 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1210
1211 /*Clear control*/
1212 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1213
1214 /*Switch into Rich Text mode*/
1215 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1216 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1217
1218 /*Print "wine" in normal formatting into the control*/
1219 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1220
1221 /*Paste italicized "wine" into the control*/
1222 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1223
1224 /*Select text from the first "wine" string*/
1225 cr.cpMin = 1;
1226 cr.cpMax = 3;
1227 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1228
1229 /*Retrieve its formatting*/
1230 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1231
1232 /*Select text from the second "wine" string*/
1233 cr.cpMin = 6;
1234 cr.cpMax = 7;
1235 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1236
1237 /*Retrieve its formatting*/
1238 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1239
1240 /*Test that the two formattings are not the same*/
1241 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1242 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1243 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1244
1245 DestroyWindow(hwndRichEdit);
1246 }
1247
1248 static void test_SETPARAFORMAT(void)
1249 {
1250 HWND hwndRichEdit = new_richedit(NULL);
1251 PARAFORMAT2 fmt;
1252 HRESULT ret;
1253 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1254 fmt.cbSize = sizeof(PARAFORMAT2);
1255 fmt.dwMask = PFM_ALIGNMENT;
1256 fmt.wAlignment = PFA_LEFT;
1257
1258 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1259 ok(ret != 0, "expected non-zero got %d\n", ret);
1260
1261 fmt.cbSize = sizeof(PARAFORMAT2);
1262 fmt.dwMask = -1;
1263 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1264 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1265 * between richedit different native builds of riched20.dll
1266 * used on different Windows versions. */
1267 ret &= ~PFM_TABLEROWDELIMITER;
1268 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1269
1270 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1271 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1272
1273 DestroyWindow(hwndRichEdit);
1274 }
1275
1276 static void test_TM_PLAINTEXT(void)
1277 {
1278 /*Tests plain text properties*/
1279
1280 HWND hwndRichEdit = new_richedit(NULL);
1281 CHARFORMAT2A cf2, cf2test;
1282 CHARRANGE cr;
1283 int rc = 0;
1284
1285 /*Switch to plain text mode*/
1286
1287 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1288 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1289
1290 /*Fill control with text*/
1291
1292 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1293
1294 /*Select some text and bold it*/
1295
1296 cr.cpMin = 10;
1297 cr.cpMax = 20;
1298 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1299 cf2.cbSize = sizeof(CHARFORMAT2A);
1300 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1301
1302 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1303 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1304
1305 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1306 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1307
1308 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1309 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1310
1311 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1312 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1313
1314 /*Get the formatting of those characters*/
1315
1316 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1317
1318 /*Get the formatting of some other characters*/
1319 cf2test.cbSize = sizeof(CHARFORMAT2A);
1320 cr.cpMin = 21;
1321 cr.cpMax = 30;
1322 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1323 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1324
1325 /*Test that they are the same as plain text allows only one formatting*/
1326
1327 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1328 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1329 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1330
1331 /*Fill the control with a "wine" string, which when inserted will be bold*/
1332
1333 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1334
1335 /*Copy the bolded "wine" string*/
1336
1337 cr.cpMin = 0;
1338 cr.cpMax = 4;
1339 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1340 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1341
1342 /*Swap back to rich text*/
1343
1344 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1345 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1346
1347 /*Set the default formatting to bold italics*/
1348
1349 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1350 cf2.dwMask |= CFM_ITALIC;
1351 cf2.dwEffects ^= CFE_ITALIC;
1352 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1353 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1354
1355 /*Set the text in the control to "wine", which will be bold and italicized*/
1356
1357 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1358
1359 /*Paste the plain text "wine" string, which should take the insert
1360 formatting, which at the moment is bold italics*/
1361
1362 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1363
1364 /*Select the first "wine" string and retrieve its formatting*/
1365
1366 cr.cpMin = 1;
1367 cr.cpMax = 3;
1368 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1369 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1370
1371 /*Select the second "wine" string and retrieve its formatting*/
1372
1373 cr.cpMin = 5;
1374 cr.cpMax = 7;
1375 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1376 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1377
1378 /*Compare the two formattings. They should be the same.*/
1379
1380 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1381 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1382 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1383 DestroyWindow(hwndRichEdit);
1384 }
1385
1386 static void test_WM_GETTEXT(void)
1387 {
1388 HWND hwndRichEdit = new_richedit(NULL);
1389 static const char text[] = "Hello. My name is RichEdit!";
1390 static const char text2[] = "Hello. My name is RichEdit!\r";
1391 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1392 char buffer[1024] = {0};
1393 int result;
1394
1395 /* Baseline test with normal-sized buffer */
1396 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1397 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1398 ok(result == lstrlenA(buffer),
1399 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1400 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1401 result = strcmp(buffer,text);
1402 ok(result == 0,
1403 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1404
1405 /* Test for returned value of WM_GETTEXTLENGTH */
1406 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1407 ok(result == lstrlenA(text),
1408 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1409 result, lstrlenA(text));
1410
1411 /* Test for behavior in overflow case */
1412 memset(buffer, 0, 1024);
1413 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1414 ok(result == 0 ||
1415 result == lstrlenA(text) - 1, /* XP, win2k3 */
1416 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1417 result = strcmp(buffer,text);
1418 if (result)
1419 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1420 ok(result == 0,
1421 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1422
1423 /* Baseline test with normal-sized buffer and carriage return */
1424 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1425 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1426 ok(result == lstrlenA(buffer),
1427 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1428 result = strcmp(buffer,text2_after);
1429 ok(result == 0,
1430 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1431
1432 /* Test for returned value of WM_GETTEXTLENGTH */
1433 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1434 ok(result == lstrlenA(text2_after),
1435 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1436 result, lstrlenA(text2_after));
1437
1438 /* Test for behavior of CRLF conversion in case of overflow */
1439 memset(buffer, 0, 1024);
1440 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1441 ok(result == 0 ||
1442 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1443 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1444 result = strcmp(buffer,text2);
1445 if (result)
1446 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1447 ok(result == 0,
1448 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1449
1450 DestroyWindow(hwndRichEdit);
1451 }
1452
1453 static void test_EM_GETTEXTRANGE(void)
1454 {
1455 HWND hwndRichEdit = new_richedit(NULL);
1456 const char * text1 = "foo bar\r\nfoo bar";
1457 const char * text2 = "foo bar\rfoo bar";
1458 const char * expect = "bar\rfoo";
1459 char buffer[1024] = {0};
1460 LRESULT result;
1461 TEXTRANGEA textRange;
1462
1463 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1464
1465 textRange.lpstrText = buffer;
1466 textRange.chrg.cpMin = 4;
1467 textRange.chrg.cpMax = 11;
1468 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1469 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1470 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1471
1472 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1473
1474 textRange.lpstrText = buffer;
1475 textRange.chrg.cpMin = 4;
1476 textRange.chrg.cpMax = 11;
1477 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1478 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1479 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1480
1481 /* cpMax of text length is used instead of -1 in this case */
1482 textRange.lpstrText = buffer;
1483 textRange.chrg.cpMin = 0;
1484 textRange.chrg.cpMax = -1;
1485 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1486 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488
1489 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1490 textRange.lpstrText = buffer;
1491 textRange.chrg.cpMin = -1;
1492 textRange.chrg.cpMax = 1;
1493 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1494 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1495 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1496
1497 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1498 textRange.lpstrText = buffer;
1499 textRange.chrg.cpMin = 1;
1500 textRange.chrg.cpMax = -1;
1501 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1502 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1503 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1504
1505 /* no end character is copied if cpMax - cpMin < 0 */
1506 textRange.lpstrText = buffer;
1507 textRange.chrg.cpMin = 5;
1508 textRange.chrg.cpMax = 5;
1509 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1510 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1511 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1512
1513 /* cpMax of text length is used if cpMax > text length*/
1514 textRange.lpstrText = buffer;
1515 textRange.chrg.cpMin = 0;
1516 textRange.chrg.cpMax = 1000;
1517 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1518 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1519 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1520
1521 DestroyWindow(hwndRichEdit);
1522 }
1523
1524 static void test_EM_GETSELTEXT(void)
1525 {
1526 HWND hwndRichEdit = new_richedit(NULL);
1527 const char * text1 = "foo bar\r\nfoo bar";
1528 const char * text2 = "foo bar\rfoo bar";
1529 const char * expect = "bar\rfoo";
1530 char buffer[1024] = {0};
1531 LRESULT result;
1532
1533 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1534
1535 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1536 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1537 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1538 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1539
1540 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1541
1542 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1543 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1544 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1545 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1546
1547 DestroyWindow(hwndRichEdit);
1548 }
1549
1550 /* FIXME: need to test unimplemented options and robustly test wparam */
1551 static void test_EM_SETOPTIONS(void)
1552 {
1553 HWND hwndRichEdit;
1554 static const char text[] = "Hello. My name is RichEdit!";
1555 char buffer[1024] = {0};
1556 DWORD dwStyle, options, oldOptions;
1557 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1558 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1559 ES_SELECTIONBAR|ES_VERTICAL;
1560
1561 /* Test initial options. */
1562 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1563 0, 0, 200, 60, NULL, NULL,
1564 hmoduleRichEdit, NULL);
1565 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1566 RICHEDIT_CLASS20A, (int) GetLastError());
1567 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1568 ok(options == 0, "Incorrect initial options %x\n", options);
1569 DestroyWindow(hwndRichEdit);
1570
1571 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1572 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1573 0, 0, 200, 60, NULL, NULL,
1574 hmoduleRichEdit, NULL);
1575 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1576 RICHEDIT_CLASS20A, (int) GetLastError());
1577 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1578 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1579 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1580 "Incorrect initial options %x\n", options);
1581
1582 /* NEGATIVE TESTING - NO OPTIONS SET */
1583 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1584 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1585
1586 /* testing no readonly by sending 'a' to the control*/
1587 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1588 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1589 ok(buffer[0]=='a',
1590 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1591 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1592
1593 /* READONLY - sending 'a' to the control */
1594 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1595 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1596 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1597 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1598 ok(buffer[0]==text[0],
1599 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1600
1601 /* EM_SETOPTIONS changes the window style, but changing the
1602 * window style does not change the options. */
1603 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1604 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1605 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1606 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1607 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1608 /* Confirm that the text is still read only. */
1609 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1610 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1611 ok(buffer[0]==text[0],
1612 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1613
1614 oldOptions = options;
1615 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1616 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1617 ok(options == oldOptions,
1618 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1619
1620 DestroyWindow(hwndRichEdit);
1621 }
1622
1623 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1624 {
1625 CHARFORMAT2A text_format;
1626 text_format.cbSize = sizeof(text_format);
1627 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1628 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1629 return (text_format.dwEffects & CFE_LINK) != 0;
1630 }
1631
1632 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1633 {
1634 BOOL link_present = FALSE;
1635
1636 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1637 if (is_url)
1638 { /* control text is url; should get CFE_LINK */
1639 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1640 }
1641 else
1642 {
1643 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1644 }
1645 }
1646
1647 static HWND new_static_wnd(HWND parent) {
1648 return new_window("Static", 0, parent);
1649 }
1650
1651 static void test_EM_AUTOURLDETECT(void)
1652 {
1653 /* DO NOT change the properties of the first two elements. To shorten the
1654 tests, all tests after WM_SETTEXT test just the first two elements -
1655 one non-URL and one URL */
1656 struct urls_s {
1657 const char *text;
1658 BOOL is_url;
1659 } urls[12] = {
1660 {"winehq.org", FALSE},
1661 {"http://www.winehq.org", TRUE},
1662 {"http//winehq.org", FALSE},
1663 {"ww.winehq.org", FALSE},
1664 {"www.winehq.org", TRUE},
1665 {"ftp://192.168.1.1", TRUE},
1666 {"ftp//192.168.1.1", FALSE},
1667 {"mailto:your@email.com", TRUE},
1668 {"prospero:prosperoserver", TRUE},
1669 {"telnet:test", TRUE},
1670 {"news:newserver", TRUE},
1671 {"wais:waisserver", TRUE}
1672 };
1673
1674 int i, j;
1675 int urlRet=-1;
1676 HWND hwndRichEdit, parent;
1677
1678 /* All of the following should cause the URL to be detected */
1679 const char * templates_delim[] = {
1680 "This is some text with X on it",
1681 "This is some text with (X) on it",
1682 "This is some text with X\r on it",
1683 "This is some text with ---X--- on it",
1684 "This is some text with \"X\" on it",
1685 "This is some text with 'X' on it",
1686 "This is some text with 'X' on it",
1687 "This is some text with :X: on it",
1688
1689 "This text ends with X",
1690
1691 "This is some text with X) on it",
1692 "This is some text with X--- on it",
1693 "This is some text with X\" on it",
1694 "This is some text with X' on it",
1695 "This is some text with X: on it",
1696
1697 "This is some text with (X on it",
1698 "This is some text with \rX on it",
1699 "This is some text with ---X 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 };
1704 /* None of these should cause the URL to be detected */
1705 const char * templates_non_delim[] = {
1706 "This is some text with |X| on it",
1707 "This is some text with *X* on it",
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",
1713 "This is some text with \\X\\ on it",
1714 "This is some text with |X on it",
1715 "This is some text with *X 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",
1720 "This is some text with @X on it",
1721 "This is some text with \\X on it",
1722 };
1723 /* All of these cause the URL detection to be extended by one more byte,
1724 thus demonstrating that the tested character is considered as part
1725 of the URL. */
1726 const char * templates_xten_delim[] = {
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 };
1736 char buffer[1024];
1737
1738 parent = new_static_wnd(NULL);
1739 hwndRichEdit = new_richedit(parent);
1740 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1741 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1742 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1743 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1744 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1745 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1746 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1747 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1748 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1749 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1750 /* for each url, check the text to see if CFE_LINK effect is present */
1751 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1752
1753 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1754 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1755 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1756
1757 /* Link detection should happen immediately upon WM_SETTEXT */
1758 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1759 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1760 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1761 }
1762 DestroyWindow(hwndRichEdit);
1763
1764 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1765 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1766 hwndRichEdit = new_richedit(parent);
1767
1768 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1769 char * at_pos;
1770 int at_offset;
1771 int end_offset;
1772
1773 at_pos = strchr(templates_delim[j], 'X');
1774 at_offset = at_pos - templates_delim[j];
1775 memcpy(buffer, templates_delim[j], at_offset);
1776 buffer[at_offset] = '\0';
1777 strcat(buffer, urls[i].text);
1778 strcat(buffer, templates_delim[j] + at_offset + 1);
1779 end_offset = at_offset + strlen(urls[i].text);
1780
1781 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1782 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1783
1784 /* This assumes no templates start with the URL itself, and that they
1785 have at least two characters before the URL text */
1786 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1787 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1788 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1789 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1790 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1791 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1792
1793 if (urls[i].is_url)
1794 {
1795 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1796 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1797 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1798 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1799 }
1800 else
1801 {
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1806 }
1807 if (buffer[end_offset] != '\0')
1808 {
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1811 if (buffer[end_offset +1] != '\0')
1812 {
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1815 }
1816 }
1817 }
1818
1819 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1820 char * at_pos;
1821 int at_offset;
1822 int end_offset;
1823
1824 at_pos = strchr(templates_non_delim[j], 'X');
1825 at_offset = at_pos - templates_non_delim[j];
1826 memcpy(buffer, templates_non_delim[j], at_offset);
1827 buffer[at_offset] = '\0';
1828 strcat(buffer, urls[i].text);
1829 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1830 end_offset = at_offset + strlen(urls[i].text);
1831
1832 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1833 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1834
1835 /* This assumes no templates start with the URL itself, and that they
1836 have at least two characters before the URL text */
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1841 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1842 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1843
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1848 if (buffer[end_offset] != '\0')
1849 {
1850 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1851 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1852 if (buffer[end_offset +1] != '\0')
1853 {
1854 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1855 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1856 }
1857 }
1858 }
1859
1860 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1861 char * at_pos;
1862 int at_offset;
1863 int end_offset;
1864
1865 at_pos = strchr(templates_xten_delim[j], 'X');
1866 at_offset = at_pos - templates_xten_delim[j];
1867 memcpy(buffer, templates_xten_delim[j], at_offset);
1868 buffer[at_offset] = '\0';
1869 strcat(buffer, urls[i].text);
1870 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1871 end_offset = at_offset + strlen(urls[i].text);
1872
1873 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1874 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1875
1876 /* This assumes no templates start with the URL itself, and that they
1877 have at least two characters before the URL text */
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1880 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1881 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1882 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1883 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1884
1885 if (urls[i].is_url)
1886 {
1887 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1888 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1889 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1890 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1891 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1892 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1893 }
1894 else
1895 {
1896 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1897 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1902 }
1903 if (buffer[end_offset +1] != '\0')
1904 {
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1907 if (buffer[end_offset +2] != '\0')
1908 {
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1911 }
1912 }
1913 }
1914
1915 DestroyWindow(hwndRichEdit);
1916 hwndRichEdit = NULL;
1917 }
1918
1919 /* Test detection of URLs within normal text - WM_CHAR case. */
1920 /* Test only the first two URL examples for brevity */
1921 for (i = 0; i < 2; i++) {
1922 hwndRichEdit = new_richedit(parent);
1923
1924 /* Also for brevity, test only the first three delimiters */
1925 for (j = 0; j < 3; j++) {
1926 char * at_pos;
1927 int at_offset;
1928 int end_offset;
1929 int u, v;
1930
1931 at_pos = strchr(templates_delim[j], 'X');
1932 at_offset = at_pos - templates_delim[j];
1933 end_offset = at_offset + strlen(urls[i].text);
1934
1935 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1936 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
1937 for (u = 0; templates_delim[j][u]; u++) {
1938 if (templates_delim[j][u] == '\r') {
1939 simulate_typing_characters(hwndRichEdit, "\r");
1940 } else if (templates_delim[j][u] != 'X') {
1941 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1942 } else {
1943 for (v = 0; urls[i].text[v]; v++) {
1944 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1945 }
1946 }
1947 }
1948 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1949
1950 /* This assumes no templates start with the URL itself, and that they
1951 have at least two characters before the URL text */
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1956 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1957 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1958
1959 if (urls[i].is_url)
1960 {
1961 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1962 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1963 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1964 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1965 }
1966 else
1967 {
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1970 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1971 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1972 }
1973 if (buffer[end_offset] != '\0')
1974 {
1975 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1976 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1977 if (buffer[end_offset +1] != '\0')
1978 {
1979 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1980 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1981 }
1982 }
1983
1984 /* The following will insert a paragraph break after the first character
1985 of the URL candidate, thus breaking the URL. It is expected that the
1986 CFE_LINK attribute should break across both pieces of the URL */
1987 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1988 simulate_typing_characters(hwndRichEdit, "\r");
1989 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1990
1991 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1992 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1993 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1994 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1995 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1996 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1997
1998 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1999 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2000 /* end_offset moved because of paragraph break */
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2003 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2004 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2005 {
2006 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2007 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2008 if (buffer[end_offset +2] != '\0')
2009 {
2010 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2011 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2012 }
2013 }
2014
2015 /* The following will remove the just-inserted paragraph break, thus
2016 restoring the URL */
2017 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2018 simulate_typing_characters(hwndRichEdit, "\b");
2019 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2020
2021 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2022 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2023 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2024 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2025 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2026 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2027
2028 if (urls[i].is_url)
2029 {
2030 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2031 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2032 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2033 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2034 }
2035 else
2036 {
2037 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2038 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2041 }
2042 if (buffer[end_offset] != '\0')
2043 {
2044 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2045 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2046 if (buffer[end_offset +1] != '\0')
2047 {
2048 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2049 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2050 }
2051 }
2052 }
2053 DestroyWindow(hwndRichEdit);
2054 hwndRichEdit = NULL;
2055 }
2056
2057 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2058 /* Test just the first two URL examples for brevity */
2059 for (i = 0; i < 2; i++) {
2060 SETTEXTEX st;
2061
2062 hwndRichEdit = new_richedit(parent);
2063
2064 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2065 be detected:
2066 1) Set entire text, a la WM_SETTEXT
2067 2) Set a selection of the text to the URL
2068 3) Set a portion of the text at a time, which eventually results in
2069 an URL
2070 All of them should give equivalent results
2071 */
2072
2073 /* Set entire text in one go, like WM_SETTEXT */
2074 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2075 char * at_pos;
2076 int at_offset;
2077 int end_offset;
2078
2079 st.codepage = CP_ACP;
2080 st.flags = ST_DEFAULT;
2081
2082 at_pos = strchr(templates_delim[j], 'X');
2083 at_offset = at_pos - templates_delim[j];
2084 memcpy(buffer, templates_delim[j], at_offset);
2085 buffer[at_offset] = '\0';
2086 strcat(buffer, urls[i].text);
2087 strcat(buffer, templates_delim[j] + at_offset + 1);
2088 end_offset = at_offset + strlen(urls[i].text);
2089
2090 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2091 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2092
2093 /* This assumes no templates start with the URL itself, and that they
2094 have at least two characters before the URL text */
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2099 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2100 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2101
2102 if (urls[i].is_url)
2103 {
2104 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2105 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2106 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2107 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2108 }
2109 else
2110 {
2111 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2112 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2113 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2114 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2115 }
2116 if (buffer[end_offset] != '\0')
2117 {
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2120 if (buffer[end_offset +1] != '\0')
2121 {
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2124 }
2125 }
2126 }
2127
2128 /* Set selection with X to the URL */
2129 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2130 char * at_pos;
2131 int at_offset;
2132 int end_offset;
2133
2134 at_pos = strchr(templates_delim[j], 'X');
2135 at_offset = at_pos - templates_delim[j];
2136 end_offset = at_offset + strlen(urls[i].text);
2137
2138 st.codepage = CP_ACP;
2139 st.flags = ST_DEFAULT;
2140 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2141 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2142 st.flags = ST_SELECTION;
2143 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2144 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2145 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2146
2147 /* This assumes no templates start with the URL itself, and that they
2148 have at least two characters before the URL text */
2149 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2150 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2151 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2152 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2153 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2154 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2155
2156 if (urls[i].is_url)
2157 {
2158 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2159 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2160 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2161 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2162 }
2163 else
2164 {
2165 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2166 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2167 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2168 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2169 }
2170 if (buffer[end_offset] != '\0')
2171 {
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2174 if (buffer[end_offset +1] != '\0')
2175 {
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2178 }
2179 }
2180 }
2181
2182 /* Set selection with X to the first character of the URL, then the rest */
2183 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2184 char * at_pos;
2185 int at_offset;
2186 int end_offset;
2187
2188 at_pos = strchr(templates_delim[j], 'X');
2189 at_offset = at_pos - templates_delim[j];
2190 end_offset = at_offset + strlen(urls[i].text);
2191
2192 strcpy(buffer, "YY");
2193 buffer[0] = urls[i].text[0];
2194
2195 st.codepage = CP_ACP;
2196 st.flags = ST_DEFAULT;
2197 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2198 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2199 st.flags = ST_SELECTION;
2200 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2201 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2202 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2203 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2204 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2205
2206 /* This assumes no templates start with the URL itself, and that they
2207 have at least two characters before the URL text */
2208 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2209 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2210 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2211 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2214
2215 if (urls[i].is_url)
2216 {
2217 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2218 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2219 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2220 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2221 }
2222 else
2223 {
2224 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2225 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2226 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2227 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2228 }
2229 if (buffer[end_offset] != '\0')
2230 {
2231 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2232 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2233 if (buffer[end_offset +1] != '\0')
2234 {
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2237 }
2238 }
2239 }
2240
2241 DestroyWindow(hwndRichEdit);
2242 hwndRichEdit = NULL;
2243 }
2244
2245 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2246 /* Test just the first two URL examples for brevity */
2247 for (i = 0; i < 2; i++) {
2248 hwndRichEdit = new_richedit(parent);
2249
2250 /* Set selection with X to the URL */
2251 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2252 char * at_pos;
2253 int at_offset;
2254 int end_offset;
2255
2256 at_pos = strchr(templates_delim[j], 'X');
2257 at_offset = at_pos - templates_delim[j];
2258 end_offset = at_offset + strlen(urls[i].text);
2259
2260 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2261 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2262 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2263 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2264 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2265
2266 /* This assumes no templates start with the URL itself, and that they
2267 have at least two characters before the URL text */
2268 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2269 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2272 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2273 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2274
2275 if (urls[i].is_url)
2276 {
2277 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2278 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2279 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2280 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2281 }
2282 else
2283 {
2284 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2285 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2286 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2287 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2288 }
2289 if (buffer[end_offset] != '\0')
2290 {
2291 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2292 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2293 if (buffer[end_offset +1] != '\0')
2294 {
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2297 }
2298 }
2299 }
2300
2301 /* Set selection with X to the first character of the URL, then the rest */
2302 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2303 char * at_pos;
2304 int at_offset;
2305 int end_offset;
2306
2307 at_pos = strchr(templates_delim[j], 'X');
2308 at_offset = at_pos - templates_delim[j];
2309 end_offset = at_offset + strlen(urls[i].text);
2310
2311 strcpy(buffer, "YY");
2312 buffer[0] = urls[i].text[0];
2313
2314 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2315 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2316 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2317 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2318 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2319 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2320 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2321
2322 /* This assumes no templates start with the URL itself, and that they
2323 have at least two characters before the URL text */
2324 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2325 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2326 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2327 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2328 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2329 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2330
2331 if (urls[i].is_url)
2332 {
2333 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2334 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2335 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2336 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2337 }
2338 else
2339 {
2340 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2341 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2342 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2343 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2344 }
2345 if (buffer[end_offset] != '\0')
2346 {
2347 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2348 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2349 if (buffer[end_offset +1] != '\0')
2350 {
2351 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2352 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2353 }
2354 }
2355 }
2356
2357 DestroyWindow(hwndRichEdit);
2358 hwndRichEdit = NULL;
2359 }
2360
2361 DestroyWindow(parent);
2362 }
2363
2364 static void test_EM_SCROLL(void)
2365 {
2366 int i, j;
2367 int r; /* return value */
2368 int expr; /* expected return value */
2369 HWND hwndRichEdit = new_richedit(NULL);
2370 int y_before, y_after; /* units of lines of text */
2371
2372 /* test a richedit box containing a single line of text */
2373 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2374 expr = 0x00010000;
2375 for (i = 0; i < 4; i++) {
2376 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2377
2378 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2379 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2380 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2381 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2382 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2383 "(i == %d)\n", y_after, i);
2384 }
2385
2386 /*
2387 * test a richedit box that will scroll. There are two general
2388 * cases: the case without any long lines and the case with a long
2389 * line.
2390 */
2391 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2392 if (i == 0)
2393 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2394 else
2395 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2396 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2397 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2398 "LONG LINE \nb\nc\nd\ne");
2399 for (j = 0; j < 12; j++) /* reset scroll position to top */
2400 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2401
2402 /* get first visible line */
2403 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2404 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2405
2406 /* get new current first visible line */
2407 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2408
2409 ok(((r & 0xffffff00) == 0x00010000) &&
2410 ((r & 0x000000ff) != 0x00000000),
2411 "EM_SCROLL page down didn't scroll by a small positive number of "
2412 "lines (r == 0x%08x)\n", r);
2413 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2414 "(line %d scrolled to line %d\n", y_before, y_after);
2415
2416 y_before = y_after;
2417
2418 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2419 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2420 ok(((r & 0xffffff00) == 0x0001ff00),
2421 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2422 "(r == 0x%08x)\n", r);
2423 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2424 "%d scrolled to line %d\n", y_before, y_after);
2425
2426 y_before = y_after;
2427
2428 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2429
2430 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2431
2432 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2433 "(r == 0x%08x)\n", r);
2434 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2435 "1 line (%d scrolled to %d)\n", y_before, y_after);
2436
2437 y_before = y_after;
2438
2439 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2440
2441 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2442
2443 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2444 "(r == 0x%08x)\n", r);
2445 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2446 "line (%d scrolled to %d)\n", y_before, y_after);
2447
2448 y_before = y_after;
2449
2450 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2451 SB_LINEUP, 0); /* lineup beyond top */
2452
2453 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2454
2455 ok(r == 0x00010000,
2456 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2457 ok(y_before == y_after,
2458 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2459
2460 y_before = y_after;
2461
2462 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2463 SB_PAGEUP, 0);/*page up beyond top */
2464
2465 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2466
2467 ok(r == 0x00010000,
2468 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2469 ok(y_before == y_after,
2470 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2471
2472 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2473 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2474 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2475 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2476 SB_PAGEDOWN, 0); /* page down beyond bot */
2477 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2478
2479 ok(r == 0x00010000,
2480 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2481 ok(y_before == y_after,
2482 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2483 y_before, y_after);
2484
2485 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2486 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2487 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2488
2489 ok(r == 0x00010000,
2490 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2491 ok(y_before == y_after,
2492 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2493 y_before, y_after);
2494 }
2495 DestroyWindow(hwndRichEdit);
2496 }
2497
2498 static unsigned int recursionLevel = 0;
2499 static unsigned int WM_SIZE_recursionLevel = 0;
2500 static BOOL bailedOutOfRecursion = FALSE;
2501 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2502
2503 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2504 {
2505 LRESULT r;
2506
2507 if (bailedOutOfRecursion) return 0;
2508 if (recursionLevel >= 32) {
2509 bailedOutOfRecursion = TRUE;
2510 return 0;
2511 }
2512
2513 recursionLevel++;
2514 switch (message) {
2515 case WM_SIZE:
2516 WM_SIZE_recursionLevel++;
2517 r = richeditProc(hwnd, message, wParam, lParam);
2518 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2519 ShowScrollBar(hwnd, SB_VERT, TRUE);
2520 WM_SIZE_recursionLevel--;
2521 break;
2522 default:
2523 r = richeditProc(hwnd, message, wParam, lParam);
2524 break;
2525 }
2526 recursionLevel--;
2527 return r;
2528 }
2529
2530 static void test_scrollbar_visibility(void)
2531 {
2532 HWND hwndRichEdit;
2533 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2534 SCROLLINFO si;
2535 WNDCLASSA cls;
2536 BOOL r;
2537
2538 /* These tests show that richedit should temporarily refrain from automatically
2539 hiding or showing its scrollbars (vertical at least) when an explicit request
2540 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2541 Some applications depend on forced showing (when otherwise richedit would
2542 hide the vertical scrollbar) and are thrown on an endless recursive loop
2543 if richedit auto-hides the scrollbar again. Apparently they never heard of
2544 the ES_DISABLENOSCROLL style... */
2545
2546 hwndRichEdit = new_richedit(NULL);
2547
2548 /* Test default scrollbar visibility behavior */
2549 memset(&si, 0, sizeof(si));
2550 si.cbSize = sizeof(si);
2551 si.fMask = SIF_PAGE | SIF_RANGE;
2552 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2553 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2554 "Vertical scrollbar is visible, should be invisible.\n");
2555 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2556 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2557 si.nPage, si.nMin, si.nMax);
2558
2559 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2560 memset(&si, 0, sizeof(si));
2561 si.cbSize = sizeof(si);
2562 si.fMask = SIF_PAGE | SIF_RANGE;
2563 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2564 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2565 "Vertical scrollbar is visible, should be invisible.\n");
2566 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2567 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2568 si.nPage, si.nMin, si.nMax);
2569
2570 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2571 memset(&si, 0, sizeof(si));
2572 si.cbSize = sizeof(si);
2573 si.fMask = SIF_PAGE | SIF_RANGE;
2574 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2575 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2576 "Vertical scrollbar is invisible, should be visible.\n");
2577 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2578 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2579 si.nPage, si.nMin, si.nMax);
2580
2581 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2582 even though it hides the scrollbar */
2583 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2584 memset(&si, 0, sizeof(si));
2585 si.cbSize = sizeof(si);
2586 si.fMask = SIF_PAGE | SIF_RANGE;
2587 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2588 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2589 "Vertical scrollbar is visible, should be invisible.\n");
2590 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2591 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2592 si.nPage, si.nMin, si.nMax);
2593
2594 /* Setting non-scrolling text again does *not* reset scrollbar range */
2595 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2596 memset(&si, 0, sizeof(si));
2597 si.cbSize = sizeof(si);
2598 si.fMask = SIF_PAGE | SIF_RANGE;
2599 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2600 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2601 "Vertical scrollbar is visible, should be invisible.\n");
2602 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2603 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2604 si.nPage, si.nMin, si.nMax);
2605
2606 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2607 memset(&si, 0, sizeof(si));
2608 si.cbSize = sizeof(si);
2609 si.fMask = SIF_PAGE | SIF_RANGE;
2610 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2611 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2612 "Vertical scrollbar is visible, should be invisible.\n");
2613 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2614 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2615 si.nPage, si.nMin, si.nMax);
2616
2617 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2618 memset(&si, 0, sizeof(si));
2619 si.cbSize = sizeof(si);
2620 si.fMask = SIF_PAGE | SIF_RANGE;
2621 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2622 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2623 "Vertical scrollbar is visible, should be invisible.\n");
2624 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2625 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2626 si.nPage, si.nMin, si.nMax);
2627
2628 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2629 memset(&si, 0, sizeof(si));
2630 si.cbSize = sizeof(si);
2631 si.fMask = SIF_PAGE | SIF_RANGE;
2632 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2633 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2634 "Vertical scrollbar is visible, should be invisible.\n");
2635 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2636 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2637 si.nPage, si.nMin, si.nMax);
2638
2639 DestroyWindow(hwndRichEdit);
2640
2641 /* Test again, with ES_DISABLENOSCROLL style */
2642 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2643
2644 /* Test default scrollbar visibility behavior */
2645 memset(&si, 0, sizeof(si));
2646 si.cbSize = sizeof(si);
2647 si.fMask = SIF_PAGE | SIF_RANGE;
2648 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2649 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2650 "Vertical scrollbar is invisible, should be visible.\n");
2651 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2652 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2653 si.nPage, si.nMin, si.nMax);
2654
2655 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2656 memset(&si, 0, sizeof(si));
2657 si.cbSize = sizeof(si);
2658 si.fMask = SIF_PAGE | SIF_RANGE;
2659 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2660 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2661 "Vertical scrollbar is invisible, should be visible.\n");
2662 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2663 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2664 si.nPage, si.nMin, si.nMax);
2665
2666 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2667 memset(&si, 0, sizeof(si));
2668 si.cbSize = sizeof(si);
2669 si.fMask = SIF_PAGE | SIF_RANGE;
2670 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2671 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2672 "Vertical scrollbar is invisible, should be visible.\n");
2673 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2674 "reported page/range is %d (%d..%d)\n",
2675 si.nPage, si.nMin, si.nMax);
2676
2677 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2678 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2679 memset(&si, 0, sizeof(si));
2680 si.cbSize = sizeof(si);
2681 si.fMask = SIF_PAGE | SIF_RANGE;
2682 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2683 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2684 "Vertical scrollbar is invisible, should be visible.\n");
2685 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2686 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2687 si.nPage, si.nMin, si.nMax);
2688
2689 /* Setting non-scrolling text again does *not* reset scrollbar range */
2690 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2691 memset(&si, 0, sizeof(si));
2692 si.cbSize = sizeof(si);
2693 si.fMask = SIF_PAGE | SIF_RANGE;
2694 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2695 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2696 "Vertical scrollbar is invisible, should be visible.\n");
2697 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2698 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2699 si.nPage, si.nMin, si.nMax);
2700
2701 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2702 memset(&si, 0, sizeof(si));
2703 si.cbSize = sizeof(si);
2704 si.fMask = SIF_PAGE | SIF_RANGE;
2705 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2706 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2707 "Vertical scrollbar is invisible, should be visible.\n");
2708 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2709 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2710 si.nPage, si.nMin, si.nMax);
2711
2712 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2713 memset(&si, 0, sizeof(si));
2714 si.cbSize = sizeof(si);
2715 si.fMask = SIF_PAGE | SIF_RANGE;
2716 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2717 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2718 "Vertical scrollbar is invisible, should be visible.\n");
2719 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2720 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2721 si.nPage, si.nMin, si.nMax);
2722
2723 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2724 memset(&si, 0, sizeof(si));
2725 si.cbSize = sizeof(si);
2726 si.fMask = SIF_PAGE | SIF_RANGE;
2727 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2728 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2729 "Vertical scrollbar is invisible, should be visible.\n");
2730 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2731 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2732 si.nPage, si.nMin, si.nMax);
2733
2734 DestroyWindow(hwndRichEdit);
2735
2736 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2737 hwndRichEdit = new_richedit(NULL);
2738
2739 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2740 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
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 todo_wine {
2748 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2749 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2750 si.nPage, si.nMin, si.nMax);
2751 }
2752
2753 /* Ditto, see above */
2754 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2755 memset(&si, 0, sizeof(si));
2756 si.cbSize = sizeof(si);
2757 si.fMask = SIF_PAGE | SIF_RANGE;
2758 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2759 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2760 "Vertical scrollbar is invisible, should be visible.\n");
2761 todo_wine {
2762 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2763 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2764 si.nPage, si.nMin, si.nMax);
2765 }
2766
2767 /* Ditto, see above */
2768 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2769 memset(&si, 0, sizeof(si));
2770 si.cbSize = sizeof(si);
2771 si.fMask = SIF_PAGE | SIF_RANGE;
2772 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2773 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2774 "Vertical scrollbar is invisible, should be visible.\n");
2775 todo_wine {
2776 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2777 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2778 si.nPage, si.nMin, si.nMax);
2779 }
2780
2781 /* Ditto, see above */
2782 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2783 memset(&si, 0, sizeof(si));
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_PAGE | SIF_RANGE;
2786 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2788 "Vertical scrollbar is invisible, should be visible.\n");
2789 todo_wine {
2790 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2791 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2792 si.nPage, si.nMin, si.nMax);
2793 }
2794
2795 /* Ditto, see above */
2796 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2797 memset(&si, 0, sizeof(si));
2798 si.cbSize = sizeof(si);
2799 si.fMask = SIF_PAGE | SIF_RANGE;
2800 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2801 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2802 "Vertical scrollbar is invisible, should be visible.\n");
2803 todo_wine {
2804 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2805 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2806 si.nPage, si.nMin, si.nMax);
2807 }
2808
2809 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2810 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2811 memset(&si, 0, sizeof(si));
2812 si.cbSize = sizeof(si);
2813 si.fMask = SIF_PAGE | SIF_RANGE;
2814 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2815 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2816 "Vertical scrollbar is visible, should be invisible.\n");
2817 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2818 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2819 si.nPage, si.nMin, si.nMax);
2820
2821 DestroyWindow(hwndRichEdit);
2822
2823 hwndRichEdit = new_richedit(NULL);
2824
2825 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2826 memset(&si, 0, sizeof(si));
2827 si.cbSize = sizeof(si);
2828 si.fMask = SIF_PAGE | SIF_RANGE;
2829 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2830 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2831 "Vertical scrollbar is visible, should be invisible.\n");
2832 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2833 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2834 si.nPage, si.nMin, si.nMax);
2835
2836 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2837 memset(&si, 0, sizeof(si));
2838 si.cbSize = sizeof(si);
2839 si.fMask = SIF_PAGE | SIF_RANGE;
2840 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2841 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2842 "Vertical scrollbar is visible, should be invisible.\n");
2843 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2844 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2845 si.nPage, si.nMin, si.nMax);
2846
2847 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2848 memset(&si, 0, sizeof(si));
2849 si.cbSize = sizeof(si);
2850 si.fMask = SIF_PAGE | SIF_RANGE;
2851 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2852 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2853 "Vertical scrollbar is visible, should be invisible.\n");
2854 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2855 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2856 si.nPage, si.nMin, si.nMax);
2857
2858 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2859 memset(&si, 0, sizeof(si));
2860 si.cbSize = sizeof(si);
2861 si.fMask = SIF_PAGE | SIF_RANGE;
2862 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2863 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2864 "Vertical scrollbar is visible, should be invisible.\n");
2865 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2866 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2867 si.nPage, si.nMin, si.nMax);
2868
2869 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2870 memset(&si, 0, sizeof(si));
2871 si.cbSize = sizeof(si);
2872 si.fMask = SIF_PAGE | SIF_RANGE;
2873 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2874 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2875 "Vertical scrollbar is invisible, should be visible.\n");
2876 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2877 "reported page/range is %d (%d..%d)\n",
2878 si.nPage, si.nMin, si.nMax);
2879
2880 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2881 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2882 memset(&si, 0, sizeof(si));
2883 si.cbSize = sizeof(si);
2884 si.fMask = SIF_PAGE | SIF_RANGE;
2885 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2886 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2887 "Vertical scrollbar is visible, should be invisible.\n");
2888 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2889 "reported page/range is %d (%d..%d)\n",
2890 si.nPage, si.nMin, si.nMax);
2891
2892 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2893 memset(&si, 0, sizeof(si));
2894 si.cbSize = sizeof(si);
2895 si.fMask = SIF_PAGE | SIF_RANGE;
2896 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2897 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2898 "Vertical scrollbar is visible, should be invisible.\n");
2899 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2900 "reported page/range is %d (%d..%d)\n",
2901 si.nPage, si.nMin, si.nMax);
2902
2903 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2904 EM_SCROLL will make visible any forcefully invisible scrollbar */
2905 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2906 memset(&si, 0, sizeof(si));
2907 si.cbSize = sizeof(si);
2908 si.fMask = SIF_PAGE | SIF_RANGE;
2909 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2910 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2911 "Vertical scrollbar is invisible, should be visible.\n");
2912 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2913 "reported page/range is %d (%d..%d)\n",
2914 si.nPage, si.nMin, si.nMax);
2915
2916 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2917 memset(&si, 0, sizeof(si));
2918 si.cbSize = sizeof(si);
2919 si.fMask = SIF_PAGE | SIF_RANGE;
2920 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2921 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2922 "Vertical scrollbar is visible, should be invisible.\n");
2923 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2924 "reported page/range is %d (%d..%d)\n",
2925 si.nPage, si.nMin, si.nMax);
2926
2927 /* Again, EM_SCROLL, with SB_LINEUP */
2928 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2929 memset(&si, 0, sizeof(si));
2930 si.cbSize = sizeof(si);
2931 si.fMask = SIF_PAGE | SIF_RANGE;
2932 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2933 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2934 "Vertical scrollbar is invisible, should be visible.\n");
2935 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2936 "reported page/range is %d (%d..%d)\n",
2937 si.nPage, si.nMin, si.nMax);
2938
2939 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2940 memset(&si, 0, sizeof(si));
2941 si.cbSize = sizeof(si);
2942 si.fMask = SIF_PAGE | SIF_RANGE;
2943 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2944 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2945 "Vertical scrollbar is visible, should be invisible.\n");
2946 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2947 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2948 si.nPage, si.nMin, si.nMax);
2949
2950 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2951 memset(&si, 0, sizeof(si));
2952 si.cbSize = sizeof(si);
2953 si.fMask = SIF_PAGE | SIF_RANGE;
2954 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2955 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2956 "Vertical scrollbar is invisible, should be visible.\n");
2957 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2958 "reported page/range is %d (%d..%d)\n",
2959 si.nPage, si.nMin, si.nMax);
2960
2961 DestroyWindow(hwndRichEdit);
2962
2963
2964 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
2965 hwndRichEdit = new_richedit(NULL);
2966
2967 #define ENABLE_WS_VSCROLL(hwnd) \
2968 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2969 #define DISABLE_WS_VSCROLL(hwnd) \
2970 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2971
2972 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2973 ENABLE_WS_VSCROLL(hwndRichEdit);
2974 memset(&si, 0, sizeof(si));
2975 si.cbSize = sizeof(si);
2976 si.fMask = SIF_PAGE | SIF_RANGE;
2977 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2978 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2979 "Vertical scrollbar is invisible, should be visible.\n");
2980 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2981 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2982 si.nPage, si.nMin, si.nMax);
2983
2984 /* Ditto, see above */
2985 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2986 memset(&si, 0, sizeof(si));
2987 si.cbSize = sizeof(si);
2988 si.fMask = SIF_PAGE | SIF_RANGE;
2989 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2990 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2991 "Vertical scrollbar is invisible, should be visible.\n");
2992 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2993 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2994 si.nPage, si.nMin, si.nMax);
2995
2996 /* Ditto, see above */
2997 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2998 memset(&si, 0, sizeof(si));
2999 si.cbSize = sizeof(si);
3000 si.fMask = SIF_PAGE | SIF_RANGE;
3001 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3002 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3003 "Vertical scrollbar is invisible, should be visible.\n");
3004 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3005 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3006 si.nPage, si.nMin, si.nMax);
3007
3008 /* Ditto, see above */
3009 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3010 memset(&si, 0, sizeof(si));
3011 si.cbSize = sizeof(si);
3012 si.fMask = SIF_PAGE | SIF_RANGE;
3013 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3014 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3015 "Vertical scrollbar is invisible, should be visible.\n");
3016 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3017 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3018 si.nPage, si.nMin, si.nMax);
3019
3020 /* Ditto, see above */
3021 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3022 memset(&si, 0, sizeof(si));
3023 si.cbSize = sizeof(si);
3024 si.fMask = SIF_PAGE | SIF_RANGE;
3025 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3027 "Vertical scrollbar is invisible, should be visible.\n");
3028 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3029 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3030 si.nPage, si.nMin, si.nMax);
3031
3032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3033 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3034 memset(&si, 0, sizeof(si));
3035 si.cbSize = sizeof(si);
3036 si.fMask = SIF_PAGE | SIF_RANGE;
3037 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3038 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3039 "Vertical scrollbar is visible, should be invisible.\n");
3040 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3041 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3042 si.nPage, si.nMin, si.nMax);
3043
3044 DestroyWindow(hwndRichEdit);
3045
3046 hwndRichEdit = new_richedit(NULL);
3047
3048 DISABLE_WS_VSCROLL(hwndRichEdit);
3049 memset(&si, 0, sizeof(si));
3050 si.cbSize = sizeof(si);
3051 si.fMask = SIF_PAGE | SIF_RANGE;
3052 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3053 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3054 "Vertical scrollbar is visible, should be invisible.\n");
3055 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3056 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3057 si.nPage, si.nMin, si.nMax);
3058
3059 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3060 memset(&si, 0, sizeof(si));
3061 si.cbSize = sizeof(si);
3062 si.fMask = SIF_PAGE | SIF_RANGE;
3063 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3064 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3065 "Vertical scrollbar is visible, should be invisible.\n");
3066 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3067 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3068 si.nPage, si.nMin, si.nMax);
3069
3070 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3071 memset(&si, 0, sizeof(si));
3072 si.cbSize = sizeof(si);
3073 si.fMask = SIF_PAGE | SIF_RANGE;
3074 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3075 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3076 "Vertical scrollbar is visible, should be invisible.\n");
3077 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3078 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3079 si.nPage, si.nMin, si.nMax);
3080
3081 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3082 memset(&si, 0, sizeof(si));
3083 si.cbSize = sizeof(si);
3084 si.fMask = SIF_PAGE | SIF_RANGE;
3085 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3086 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3087 "Vertical scrollbar is visible, should be invisible.\n");
3088 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3089 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3090 si.nPage, si.nMin, si.nMax);
3091
3092 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3093 memset(&si, 0, sizeof(si));
3094 si.cbSize = sizeof(si);
3095 si.fMask = SIF_PAGE | SIF_RANGE;
3096 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3097 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3098 "Vertical scrollbar is invisible, should be visible.\n");
3099 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3100 "reported page/range is %d (%d..%d)\n",
3101 si.nPage, si.nMin, si.nMax);
3102
3103 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3104 DISABLE_WS_VSCROLL(hwndRichEdit);
3105 memset(&si, 0, sizeof(si));
3106 si.cbSize = sizeof(si);
3107 si.fMask = SIF_PAGE | SIF_RANGE;
3108 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3109 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3110 "Vertical scrollbar is visible, should be invisible.\n");
3111 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3112 "reported page/range is %d (%d..%d)\n",
3113 si.nPage, si.nMin, si.nMax);
3114
3115 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3116 memset(&si, 0, sizeof(si));
3117 si.cbSize = sizeof(si);
3118 si.fMask = SIF_PAGE | SIF_RANGE;
3119 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3120 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3121 "Vertical scrollbar is visible, should be invisible.\n");
3122 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3123 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3124 si.nPage, si.nMin, si.nMax);
3125
3126 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3127 memset(&si, 0, sizeof(si));
3128 si.cbSize = sizeof(si);
3129 si.fMask = SIF_PAGE | SIF_RANGE;
3130 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3131 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3132 "Vertical scrollbar is invisible, should be visible.\n");
3133 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3134 "reported page/range is %d (%d..%d)\n",
3135 si.nPage, si.nMin, si.nMax);
3136
3137 DISABLE_WS_VSCROLL(hwndRichEdit);
3138 memset(&si, 0, sizeof(si));
3139 si.cbSize = sizeof(si);
3140 si.fMask = SIF_PAGE | SIF_RANGE;
3141 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3142 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3143 "Vertical scrollbar is visible, should be invisible.\n");
3144 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3145 "reported page/range is %d (%d..%d)\n",
3146 si.nPage, si.nMin, si.nMax);
3147
3148 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3149 EM_SCROLL will make visible any forcefully invisible scrollbar */
3150 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3151 memset(&si, 0, sizeof(si));
3152 si.cbSize = sizeof(si);
3153 si.fMask = SIF_PAGE | SIF_RANGE;
3154 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3155 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3156 "Vertical scrollbar is invisible, should be visible.\n");
3157 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3158 "reported page/range is %d (%d..%d)\n",
3159 si.nPage, si.nMin, si.nMax);
3160
3161 DISABLE_WS_VSCROLL(hwndRichEdit);
3162 memset(&si, 0, sizeof(si));
3163 si.cbSize = sizeof(si);
3164 si.fMask = SIF_PAGE | SIF_RANGE;
3165 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3166 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3167 "Vertical scrollbar is visible, should be invisible.\n");
3168 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3169 "reported page/range is %d (%d..%d)\n",
3170 si.nPage, si.nMin, si.nMax);
3171
3172 /* Again, EM_SCROLL, with SB_LINEUP */
3173 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3174 memset(&si, 0, sizeof(si));
3175 si.cbSize = sizeof(si);
3176 si.fMask = SIF_PAGE | SIF_RANGE;
3177 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3178 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3179 "Vertical scrollbar is invisible, should be visible.\n");
3180 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3181 "reported page/range is %d (%d..%d)\n",
3182 si.nPage, si.nMin, si.nMax);
3183
3184 DestroyWindow(hwndRichEdit);
3185
3186 /* This window proc models what is going on with Corman Lisp 3.0.
3187 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3188 force the scrollbar into visibility. Recursion should NOT happen
3189 as a result of this action.
3190 */
3191 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3192 if (r) {
3193 richeditProc = cls.lpfnWndProc;
3194 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3195 cls.lpszClassName = "RicheditStupidOverride";
3196 if(!RegisterClassA(&cls)) assert(0);
3197
3198 recursionLevel = 0;
3199 WM_SIZE_recursionLevel = 0;
3200 bailedOutOfRecursion = FALSE;
3201 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3202 ok(!bailedOutOfRecursion,
3203 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3204
3205 recursionLevel = 0;
3206 WM_SIZE_recursionLevel = 0;
3207 bailedOutOfRecursion = FALSE;
3208 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3209 ok(!bailedOutOfRecursion,
3210 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3211
3212 /* Unblock window in order to process WM_DESTROY */
3213 recursionLevel = 0;
3214 bailedOutOfRecursion = FALSE;
3215 WM_SIZE_recursionLevel = 0;
3216 DestroyWindow(hwndRichEdit);
3217 }
3218 }
3219
3220 static void test_EM_SETUNDOLIMIT(void)
3221 {
3222 /* cases we test for:
3223 * default behaviour - limiting at 100 undo's
3224 * undo disabled - setting a limit of 0
3225 * undo limited - undo limit set to some to some number, like 2
3226 * bad input - sending a negative number should default to 100 undo's */
3227
3228 HWND hwndRichEdit = new_richedit(NULL);
3229 CHARRANGE cr;
3230 int i;
3231 int result;
3232
3233 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3234 cr.cpMin = 0;
3235 cr.cpMax = 1;
3236 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3237 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3238 also, multiple pastes don't combine like WM_CHAR would */
3239 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3240
3241 /* first case - check the default */
3242 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3243 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3244 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3245 for (i=0; i<100; i++) /* Undo 100 of them */
3246 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3247 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3248 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3249
3250 /* second case - cannot undo */
3251 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3252 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3253 SendMessageA(hwndRichEdit,
3254 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3255 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3256 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3257
3258 /* third case - set it to an arbitrary number */
3259 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3260 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3261 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3262 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3263 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3264 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3265 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3266 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3267 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3268 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3269 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3270 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3271 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3272 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3273
3274 /* fourth case - setting negative numbers should default to 100 undos */
3275 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3276 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3277 ok (result == 100,
3278 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3279
3280 DestroyWindow(hwndRichEdit);
3281 }
3282
3283 static void test_ES_PASSWORD(void)
3284 {
3285 /* This isn't hugely testable, so we're just going to run it through its paces */
3286
3287 HWND hwndRichEdit = new_richedit(NULL);
3288 WCHAR result;
3289
3290 /* First, check the default of a regular control */
3291 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3292 ok (result == 0,
3293 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3294
3295 /* Now, set it to something normal */
3296 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3297 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3298 ok (result == 120,
3299 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3300
3301 /* Now, set it to something odd */
3302 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3303 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3304 ok (result == 1234,
3305 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3306 DestroyWindow(hwndRichEdit);
3307 }
3308
3309 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3310 LPBYTE pbBuff,
3311 LONG cb,
3312 LONG *pcb)
3313 {
3314 char** str = (char**)dwCookie;
3315 *pcb = cb;
3316 if (*pcb > 0) {
3317 memcpy(*str, pbBuff, *pcb);
3318 *str += *pcb;
3319 }
3320 return 0;
3321 }
3322
3323 static void test_WM_SETTEXT(void)
3324 {
3325 HWND hwndRichEdit = new_richedit(NULL);
3326 const char * TestItem1 = "TestSomeText";
3327 const char * TestItem2 = "TestSomeText\r";
3328 const char * TestItem2_after = "TestSomeText\r\n";
3329 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3330 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3331 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3332 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3333 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3334 const char * TestItem5_after = "TestSomeText TestSomeText";
3335 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3336 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3337 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3338 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3339
3340 const char rtftextA[] = "{\\rtf sometext}";
3341 const char urtftextA[] = "{\\urtf sometext}";
3342 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3343 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3344 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3345
3346 char buf[1024] = {0};
3347 WCHAR bufW[1024] = {0};
3348 LRESULT result;
3349
3350 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3351 any solitary \r to be converted to \r\n on return. Properly paired
3352 \r\n are not affected. It also shows that the special sequence \r\r\n
3353 gets converted to a single space.
3354 */
3355
3356 #define TEST_SETTEXT(a, b) \
3357 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3358 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3359 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3360 ok (result == lstrlenA(buf), \
3361 "WM_GETTEXT returned %ld instead of expected %u\n", \
3362 result, lstrlenA(buf)); \
3363 result = strcmp(b, buf); \
3364 ok(result == 0, \
3365 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3366
3367 TEST_SETTEXT(TestItem1, TestItem1)
3368 TEST_SETTEXT(TestItem2, TestItem2_after)
3369 TEST_SETTEXT(TestItem3, TestItem3_after)
3370 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3371 TEST_SETTEXT(TestItem4, TestItem4_after)
3372 TEST_SETTEXT(TestItem5, TestItem5_after)
3373 TEST_SETTEXT(TestItem6, TestItem6_after)
3374 TEST_SETTEXT(TestItem7, TestItem7_after)
3375
3376 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3377 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3378 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3379 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3380 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3381 DestroyWindow(hwndRichEdit);
3382 #undef TEST_SETTEXT
3383
3384 #define TEST_SETTEXTW(a, b) \
3385 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3386 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3387 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3388 ok (result == lstrlenW(bufW), \
3389 "WM_GETTEXT returned %ld instead of expected %u\n", \
3390 result, lstrlenW(bufW)); \
3391 result = lstrcmpW(b, bufW); \
3392 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3393
3394 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3395 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3396 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3397 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3398 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3399 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3400 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3401 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3402 DestroyWindow(hwndRichEdit);
3403 #undef TEST_SETTEXTW
3404 }
3405
3406 static void test_EM_STREAMOUT(void)
3407 {
3408 HWND hwndRichEdit = new_richedit(NULL);
3409 int r;
3410 EDITSTREAM es;
3411 char buf[1024] = {0};
3412 char * p;
3413
3414 const char * TestItem1 = "TestSomeText";
3415 const char * TestItem2 = "TestSomeText\r";
3416 const char * TestItem3 = "TestSomeText\r\n";
3417
3418 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3419 p = buf;
3420 es.dwCookie = (DWORD_PTR)&p;
3421 es.dwError = 0;
3422 es.pfnCallback = test_WM_SETTEXT_esCallback;
3423 memset(buf, 0, sizeof(buf));
3424 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3425 r = strlen(buf);
3426 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3427 ok(strcmp(buf, TestItem1) == 0,
3428 "streamed text different, got %s\n", buf);
3429
3430 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3431 p = buf;
3432 es.dwCookie = (DWORD_PTR)&p;
3433 es.dwError = 0;
3434 es.pfnCallback = test_WM_SETTEXT_esCallback;
3435 memset(buf, 0, sizeof(buf));
3436 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3437 r = strlen(buf);
3438 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3439 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3440 ok(strcmp(buf, TestItem3) == 0,
3441 "streamed text different from, got %s\n", buf);
3442 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3443 p = buf;
3444 es.dwCookie = (DWORD_PTR)&p;
3445 es.dwError = 0;
3446 es.pfnCallback = test_WM_SETTEXT_esCallback;
3447 memset(buf, 0, sizeof(buf));
3448 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3449 r = strlen(buf);
3450 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3451 ok(strcmp(buf, TestItem3) == 0,
3452 "streamed text different, got %s\n", buf);
3453
3454 DestroyWindow(hwndRichEdit);
3455 }
3456
3457 static void test_EM_STREAMOUT_FONTTBL(void)
3458 {
3459 HWND hwndRichEdit = new_richedit(NULL);
3460 EDITSTREAM es;
3461 char buf[1024] = {0};
3462 char * p;
3463 char * fontTbl;
3464 int brackCount;
3465
3466 const char * TestItem = "TestSomeText";
3467
3468 /* fills in the richedit control with some text */
3469 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3470
3471 /* streams out the text in rtf format */
3472 p = buf;
3473 es.dwCookie = (DWORD_PTR)&p;
3474 es.dwError = 0;
3475 es.pfnCallback = test_WM_SETTEXT_esCallback;
3476 memset(buf, 0, sizeof(buf));
3477 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3478
3479 /* scans for \fonttbl, error if not found */
3480 fontTbl = strstr(buf, "\\fonttbl");
3481 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3482 if(fontTbl)
3483 {
3484 /* scans for terminating closing bracket */
3485 brackCount = 1;
3486 while(*fontTbl && brackCount)
3487 {
3488 if(*fontTbl == '{')
3489 brackCount++;
3490 else if(*fontTbl == '}')
3491 brackCount--;
3492 fontTbl++;
3493 }
3494 /* checks whether closing bracket is ok */
3495 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3496 if(!brackCount)
3497 {
3498 /* char before closing fonttbl block should be a closed bracket */
3499 fontTbl -= 2;
3500 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3501
3502 /* char after fonttbl block should be a crlf */
3503 fontTbl += 2;
3504 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3505 }
3506 }
3507 DestroyWindow(hwndRichEdit);
3508 }
3509
3510
3511 static void test_EM_SETTEXTEX(void)
3512 {
3513 HWND hwndRichEdit, parent;
3514 SCROLLINFO si;
3515 int sel_start, sel_end;
3516 SETTEXTEX setText;
3517 GETTEXTEX getText;
3518 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3519 'S', 'o', 'm', 'e',
3520 'T', 'e', 'x', 't', 0};
3521 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3522 't', 'S', 'o', 'm',
3523 'e', 'T', 'e', 'x',
3524 't', 't', 'S', 'o',
3525 'm', 'e', 'T', 'e',
3526 'x', 't', 0};
3527 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3528 '\r','t','S','o','m','e','T','e','x','t',0};
3529 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3530 'S', 'o', 'm', 'e',
3531 'T', 'e', 'x', 't',
3532 '\r', 0};
3533 const char * TestItem2_after = "TestSomeText\r\n";
3534 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3535 'S', 'o', 'm', 'e',
3536 'T', 'e', 'x', 't',
3537 '\r','\n','\r','\n', 0};
3538 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3539 'S', 'o', 'm', 'e',
3540 'T', 'e', 'x', 't',
3541 '\n','\n', 0};
3542 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3543 'S', 'o', 'm', 'e',
3544 'T', 'e', 'x', 't',
3545 '\r','\r', 0};
3546 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3547 'S', 'o', 'm', 'e',
3548 'T', 'e', 'x', 't',
3549 '\r','\r','\n','\r',
3550 '\n', 0};
3551 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3552 'S', 'o', 'm', 'e',
3553 'T', 'e', 'x', 't',
3554 ' ','\r', 0};
3555 #define MAX_BUF_LEN 1024
3556 WCHAR buf[MAX_BUF_LEN];
3557 char bufACP[MAX_BUF_LEN];
3558 char * p;
3559 int result;
3560 CHARRANGE cr;
3561 EDITSTREAM es;
3562 WNDCLASSA cls;
3563
3564 /* Test the scroll position with and without a parent window.
3565 *
3566 * For some reason the scroll position is 0 after EM_SETTEXTEX
3567 * with the ST_SELECTION flag only when the control has a parent
3568 * window, even though the selection is at the end. */
3569 cls.style = 0;
3570 cls.lpfnWndProc = DefWindowProcA;
3571 cls.cbClsExtra = 0;
3572 cls.cbWndExtra = 0;
3573 cls.hInstance = GetModuleHandleA(0);
3574 cls.hIcon = 0;
3575 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3576 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3577 cls.lpszMenuName = NULL;
3578 cls.lpszClassName = "ParentTestClass";
3579 if(!RegisterClassA(&cls)) assert(0);
3580
3581 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3582 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3583 ok (parent != 0, "Failed to create parent window\n");
3584
3585 hwndRichEdit = CreateWindowExA(0,
3586 RICHEDIT_CLASS20A, NULL,
3587 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3588 0, 0, 200, 60, parent, NULL,
3589 hmoduleRichEdit, NULL);
3590
3591 setText.codepage = CP_ACP;
3592 setText.flags = ST_SELECTION;
3593 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3594 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3595 si.cbSize = sizeof(si);
3596 si.fMask = SIF_ALL;
3597 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3598 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3599 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3600 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3601 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3602
3603 DestroyWindow(parent);
3604
3605 /* Test without a parent window */
3606 hwndRichEdit = new_richedit(NULL);
3607 setText.codepage = CP_ACP;
3608 setText.flags = ST_SELECTION;
3609 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3610 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3611 si.cbSize = sizeof(si);
3612 si.fMask = SIF_ALL;
3613 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3614 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3615 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3616 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3617 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3618
3619 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3620 * but this time it is because the selection is at the beginning. */
3621 setText.codepage = CP_ACP;
3622 setText.flags = ST_DEFAULT;
3623 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3624 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3625 si.cbSize = sizeof(si);
3626 si.fMask = SIF_ALL;
3627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3628 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3629 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3630 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3631 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3632
3633 setText.codepage = 1200; /* no constant for unicode */
3634 getText.codepage = 1200; /* no constant for unicode */
3635 getText.cb = MAX_BUF_LEN;
3636 getText.flags = GT_DEFAULT;
3637 getText.lpDefaultChar = NULL;
3638 getText.lpUsedDefChar = NULL;
3639
3640 setText.flags = 0;
3641 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3642 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3643 ok(lstrcmpW(buf, TestItem1) == 0,
3644 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3645
3646 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3647 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3648 */
3649 setText.codepage = 1200; /* no constant for unicode */
3650 getText.codepage = 1200; /* no constant for unicode */
3651 getText.cb = MAX_BUF_LEN;
3652 getText.flags = GT_DEFAULT;
3653 getText.lpDefaultChar = NULL;
3654 getText.lpUsedDefChar = NULL;
3655 setText.flags = 0;
3656 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3657 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3658 ok(lstrcmpW(buf, TestItem2) == 0,
3659 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3660
3661 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3662 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3663 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3664 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3665
3666 /* Baseline test for just-enough buffer space for string */
3667 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3668 getText.codepage = 1200; /* no constant for unicode */
3669 getText.flags = GT_DEFAULT;
3670 getText.lpDefaultChar = NULL;
3671 getText.lpUsedDefChar = NULL;
3672 memset(buf, 0, MAX_BUF_LEN);
3673 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3674 ok(lstrcmpW(buf, TestItem2) == 0,
3675 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3676
3677 /* When there is enough space for one character, but not both, of the CRLF
3678 pair at the end of the string, the CR is not copied at all. That is,
3679 the caller must not see CRLF pairs truncated to CR at the end of the
3680 string.
3681 */
3682 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3683 getText.codepage = 1200; /* no constant for unicode */
3684 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3685 getText.lpDefaultChar = NULL;
3686 getText.lpUsedDefChar = NULL;
3687 memset(buf, 0, MAX_BUF_LEN);
3688 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3689 ok(lstrcmpW(buf, TestItem1) == 0,
3690 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3691
3692
3693 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3694 setText.codepage = 1200; /* no constant for unicode */
3695 getText.codepage = 1200; /* no constant for unicode */
3696 getText.cb = MAX_BUF_LEN;
3697 getText.flags = GT_DEFAULT;
3698 getText.lpDefaultChar = NULL;
3699 getText.lpUsedDefChar = NULL;
3700 setText.flags = 0;
3701 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3702 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3703 ok(lstrcmpW(buf, TestItem3_after) == 0,
3704 "EM_SETTEXTEX did not convert properly\n");
3705
3706 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3707 setText.codepage = 1200; /* no constant for unicode */
3708 getText.codepage = 1200; /* no constant for unicode */
3709 getText.cb = MAX_BUF_LEN;
3710 getText.flags = GT_DEFAULT;
3711 getText.lpDefaultChar = NULL;
3712 getText.lpUsedDefChar = NULL;
3713 setText.flags = 0;
3714 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3715 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3716 ok(lstrcmpW(buf, TestItem3_after) == 0,
3717 "EM_SETTEXTEX did not convert properly\n");
3718
3719 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3720 setText.codepage = 1200; /* no constant for unicode */
3721 getText.codepage = 1200; /* no constant for unicode */
3722 getText.cb = MAX_BUF_LEN;
3723 getText.flags = GT_DEFAULT;
3724 getText.lpDefaultChar = NULL;
3725 getText.lpUsedDefChar = NULL;
3726 setText.flags = 0;
3727 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3728 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3729 ok(lstrcmpW(buf, TestItem4_after) == 0,
3730 "EM_SETTEXTEX did not convert properly\n");
3731
3732 /* !ST_SELECTION && Unicode && !\rtf */
3733 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3734 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3735
3736 ok (result == 1,
3737 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3738 ok(lstrlenW(buf) == 0,
3739 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3740
3741 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3742 setText.flags = 0;
3743 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3744 /* select some text */
3745 cr.cpMax = 1;
3746 cr.cpMin = 3;
3747 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3748 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3749 setText.flags = ST_SELECTION;
3750 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3751 ok(result == 0,
3752 "EM_SETTEXTEX with NULL lParam to replace selection"
3753 " with no text should return 0. Got %i\n",
3754 result);
3755
3756 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3757 setText.flags = 0;
3758 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3759 /* select some text */
3760 cr.cpMax = 1;
3761 cr.cpMin = 3;
3762 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3763 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3764 setText.flags = ST_SELECTION;
3765 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3766 /* get text */
3767 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3768 ok(result == lstrlenW(TestItem1),
3769 "EM_SETTEXTEX with NULL lParam to replace selection"
3770 " with no text should return 0. Got %i\n",
3771 result);
3772 ok(lstrlenW(buf) == 22,
3773 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3774 lstrlenW(buf) );
3775
3776 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3777 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3778 p = (char *)buf;
3779 es.dwCookie = (DWORD_PTR)&p;
3780 es.dwError = 0;
3781 es.pfnCallback = test_WM_SETTEXT_esCallback;
3782 memset(buf, 0, sizeof(buf));
3783 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3784 (WPARAM)(SF_RTF), (LPARAM)&es);
3785 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3786
3787 /* !ST_SELECTION && !Unicode && \rtf */
3788 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3789 getText.codepage = 1200; /* no constant for unicode */
3790 getText.cb = MAX_BUF_LEN;
3791 getText.flags = GT_DEFAULT;
3792 getText.lpDefaultChar = NULL;
3793 getText.lpUsedDefChar = NULL;
3794
3795 setText.flags = 0;
3796 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3797 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3798 ok(lstrcmpW(buf, TestItem1) == 0,
3799 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3800
3801 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3802 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3803 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3804 getText.codepage = CP_ACP;
3805 getText.cb = MAX_BUF_LEN;
3806 getText.flags = GT_DEFAULT;
3807 getText.lpDefaultChar = NULL;
3808 getText.lpUsedDefChar = NULL;
3809
3810 setText.flags = ST_SELECTION;
3811 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3812 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
3813 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3814 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3815 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3816
3817 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3818 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3819 p = (char *)buf;
3820 es.dwCookie = (DWORD_PTR)&p;
3821 es.dwError = 0;
3822 es.pfnCallback = test_WM_SETTEXT_esCallback;
3823 memset(buf, 0, sizeof(buf));
3824 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3825 (WPARAM)(SF_RTF), (LPARAM)&es);
3826 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3827
3828 /* select some text */
3829 cr.cpMax = 1;
3830 cr.cpMin = 3;
3831 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3832
3833 /* ST_SELECTION && !Unicode && \rtf */
3834 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3835 getText.codepage = 1200; /* no constant for unicode */
3836 getText.cb = MAX_BUF_LEN;
3837 getText.flags = GT_DEFAULT;
3838 getText.lpDefaultChar = NULL;
3839 getText.lpUsedDefChar = NULL;
3840
3841 setText.flags = ST_SELECTION;
3842 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3843 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3844 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3845
3846 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3847 setText.codepage = 1200; /* no constant for unicode */
3848 getText.codepage = CP_ACP;
3849 getText.cb = MAX_BUF_LEN;
3850
3851 setText.flags = 0;
3852 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
3853 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3854
3855 /* select some text */
3856 cr.cpMax = 1;
3857 cr.cpMin = 3;
3858 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3859
3860 /* ST_SELECTION && !Unicode && !\rtf */
3861 setText.codepage = CP_ACP;
3862 getText.codepage = 1200; /* no constant for unicode */
3863 getText.cb = MAX_BUF_LEN;
3864 getText.flags = GT_DEFAULT;
3865 getText.lpDefaultChar = NULL;
3866 getText.lpUsedDefChar = NULL;
3867
3868 setText.flags = ST_SELECTION;
3869 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
3870 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3871 ok(lstrcmpW(buf, TestItem1alt) == 0,
3872 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3873 " using ST_SELECTION and non-Unicode\n");
3874
3875 /* Test setting text using rich text format */
3876 setText.flags = 0;
3877 setText.codepage = CP_ACP;
3878 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3879 getText.codepage = CP_ACP;
3880 getText.cb = MAX_BUF_LEN;
3881 getText.flags = GT_DEFAULT;
3882 getText.lpDefaultChar = NULL;
3883 getText.lpUsedDefChar = NULL;
3884 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3885 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3886
3887 setText.flags = 0;
3888 setText.codepage = CP_ACP;
3889 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3890 getText.codepage = CP_ACP;
3891 getText.cb = MAX_BUF_LEN;
3892 getText.flags = GT_DEFAULT;
3893 getText.lpDefaultChar = NULL;
3894 getText.lpUsedDefChar = NULL;
3895 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3896 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3897
3898 /* test for utf8 text with BOM */
3899 setText.flags = 0;
3900 setText.codepage = CP_ACP;
3901 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3902 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3903 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3904 result = strcmp(bufACP, "TestUTF8WithBOM");
3905 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3906
3907 setText.flags = 0;
3908 setText.codepage = CP_UTF8;
3909 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3910 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3911 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3912 result = strcmp(bufACP, "TestUTF8WithBOM");
3913 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3914
3915 DestroyWindow(hwndRichEdit);
3916 }
3917
3918 static void test_EM_LIMITTEXT(void)
3919 {
3920 int ret;
3921
3922 HWND hwndRichEdit = new_richedit(NULL);
3923
3924 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3925 * about setting the length to -1 for multiline edit controls doesn't happen.
3926 */
3927
3928 /* Don't check default gettextlimit case. That's done in other tests */
3929
3930 /* Set textlimit to 100 */
3931 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
3932 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3933 ok (ret == 100,
3934 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3935
3936 /* Set textlimit to 0 */
3937 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
3938 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3939 ok (ret == 65536,
3940 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3941
3942 /* Set textlimit to -1 */
3943 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
3944 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3945 ok (ret == -1,
3946 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3947
3948 /* Set textlimit to -2 */
3949 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
3950 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3951 ok (ret == -2,
3952 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3953
3954 DestroyWindow (hwndRichEdit);
3955 }
3956
3957
3958 static void test_EM_EXLIMITTEXT(void)
3959 {
3960 int i, selBegin, selEnd, len1, len2;
3961 int result;
3962 char text[1024 + 1];
3963 char buffer[1024 + 1];
3964 int textlimit = 0; /* multiple of 100 */
3965 HWND hwndRichEdit = new_richedit(NULL);
3966
3967 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3968 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3969
3970 textlimit = 256000;
3971 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3972 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3973 /* set higher */
3974 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3975
3976 textlimit = 1000;
3977 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3978 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3979 /* set lower */
3980 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3981
3982 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3983 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3984 /* default for WParam = 0 */
3985 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3986
3987 textlimit = sizeof(text)-1;
3988 memset(text, 'W', textlimit);
3989 text[sizeof(text)-1] = 0;
3990 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3991 /* maxed out text */
3992 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3993
3994 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3995 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3996 len1 = selEnd - selBegin;
3997
3998 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3999 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4000 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4001 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4002 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4003 len2 = selEnd - selBegin;
4004
4005 ok(len1 != len2,
4006 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4007 len1,len2,i);
4008
4009 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4010 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4011 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4012 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4013 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4014 len1 = selEnd - selBegin;
4015
4016 ok(len1 != len2,
4017 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4018 len1,len2,i);
4019
4020 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4021 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4022 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4023 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4024 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4025 len2 = selEnd - selBegin;
4026
4027 ok(len1 == len2,
4028 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4029 len1,len2,i);
4030
4031 /* set text up to the limit, select all the text, then add a char */
4032 textlimit = 5;
4033 memset(text, 'W', textlimit);
4034 text[textlimit] = 0;
4035 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4036 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4037 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4038 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4039 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4040 result = strcmp(buffer, "A");
4041 ok(0 == result, "got string = \"%s\"\n", buffer);
4042
4043 /* WM_SETTEXT not limited */
4044 textlimit = 10;
4045 memset(text, 'W', textlimit);
4046 text[textlimit] = 0;
4047 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4048 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4049 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4050 i = strlen(buffer);
4051 ok(10 == i, "expected 10 chars\n");
4052 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4053 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4054
4055 /* try inserting more text at end */
4056 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4057 ok(0 == i, "WM_CHAR wasn't processed\n");
4058 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4059 i = strlen(buffer);
4060 ok(10 == i, "expected 10 chars, got %i\n", i);
4061 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4062 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4063
4064 /* try inserting text at beginning */
4065 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4066 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4067 ok(0 == i, "WM_CHAR wasn't processed\n");
4068 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4069 i = strlen(buffer);
4070 ok(10 == i, "expected 10 chars, got %i\n", i);
4071 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4072 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4073
4074 /* WM_CHAR is limited */
4075 textlimit = 1;
4076 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4077 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4078 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4079 ok(0 == i, "WM_CHAR wasn't processed\n");
4080 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4081 ok(0 == i, "WM_CHAR wasn't processed\n");
4082 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4083 i = strlen(buffer);
4084 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4085
4086 DestroyWindow(hwndRichEdit);
4087 }
4088
4089 static void test_EM_GETLIMITTEXT(void)
4090 {
4091 int i;
4092 HWND hwndRichEdit = new_richedit(NULL);
4093
4094 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4095 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4096
4097 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4098 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4099 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4100
4101 DestroyWindow(hwndRichEdit);
4102 }
4103
4104 static void test_WM_SETFONT(void)
4105 {
4106 /* There is no invalid input or error conditions for this function.
4107 * NULL wParam and lParam just fall back to their default values
4108 * It should be noted that even if you use a gibberish name for your fonts
4109 * here, it will still work because the name is stored. They will display as
4110 * System, but will report their name to be whatever they were created as */
4111
4112 HWND hwndRichEdit = new_richedit(NULL);
4113 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4114 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4115 FF_DONTCARE, "Marlett");
4116 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4117 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4118 FF_DONTCARE, "MS Sans Serif");
4119 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4120 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4121 FF_DONTCARE, "Courier");
4122 LOGFONTA sentLogFont;
4123 CHARFORMAT2A returnedCF2A;
4124
4125 returnedCF2A.cbSize = sizeof(returnedCF2A);
4126
4127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4128 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4129 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4130
4131 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4132 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4133 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4134 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4135
4136 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4137 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4138 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4139 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4140 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4141 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4142
4143 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4144 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4145 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4146 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4147 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4148 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4149
4150 /* This last test is special since we send in NULL. We clear the variables
4151 * and just compare to "System" instead of the sent in font name. */
4152 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4153 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4154 returnedCF2A.cbSize = sizeof(returnedCF2A);
4155
4156 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4157 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4158 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4159 ok (!strcmp("System",returnedCF2A.szFaceName),
4160 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4161
4162 DestroyWindow(hwndRichEdit);
4163 }
4164
4165
4166 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4167 LPBYTE pbBuff,
4168 LONG cb,
4169 LONG *pcb)
4170 {
4171 const char** str = (const char**)dwCookie;
4172 int size = strlen(*str);
4173 if(size > 3) /* let's make it piecemeal for fun */
4174 size = 3;
4175 *pcb = cb;
4176 if (*pcb > size) {
4177 *pcb = size;
4178 }
4179 if (*pcb > 0) {
4180 memcpy(pbBuff, *str, *pcb);
4181 *str += *pcb;
4182 }
4183 return 0;
4184 }
4185
4186 static void test_EM_GETMODIFY(void)
4187 {
4188 HWND hwndRichEdit = new_richedit(NULL);
4189 LRESULT result;
4190 SETTEXTEX setText;
4191 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4192 'S', 'o', 'm', 'e',
4193 'T', 'e', 'x', 't', 0};
4194 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4195 'S', 'o', 'm', 'e',
4196 'O', 't', 'h', 'e', 'r',
4197 'T', 'e', 'x', 't', 0};
4198 const char* streamText = "hello world";
4199 CHARFORMAT2A cf2;
4200 PARAFORMAT2 pf2;
4201 EDITSTREAM es;
4202
4203 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4204 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4205 FF_DONTCARE, "Courier");
4206
4207 setText.codepage = 1200; /* no constant for unicode */
4208 setText.flags = ST_KEEPUNDO;
4209
4210
4211 /* modify flag shouldn't be set when richedit is first created */
4212 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4213 ok (result == 0,
4214 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4215
4216 /* setting modify flag should actually set it */
4217 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4218 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4219 ok (result != 0,
4220 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4221
4222 /* clearing modify flag should actually clear it */
4223 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4224 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4225 ok (result == 0,
4226 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4227
4228 /* setting font doesn't change modify flag */
4229 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4231 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4232 ok (result == 0,
4233 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4234
4235 /* setting text should set modify flag */
4236 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4237 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4238 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4239 ok (result != 0,
4240 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4241
4242 /* undo previous text doesn't reset modify flag */
4243 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4244 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4245 ok (result != 0,
4246 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4247
4248 /* set text with no flag to keep undo stack should not set modify flag */
4249 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4250 setText.flags = 0;
4251 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4252 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4253 ok (result == 0,
4254 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4255
4256 /* WM_SETTEXT doesn't modify */
4257 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4258 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4259 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4260 ok (result == 0,
4261 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4262
4263 /* clear the text */
4264 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4265 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4266 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4267 ok (result == 0,
4268 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4269
4270 /* replace text */
4271 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4272 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4273 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4274 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4275 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4276 ok (result != 0,
4277 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4278
4279 /* copy/paste text 1 */
4280 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4282 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4283 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4284 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4285 ok (result != 0,
4286 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4287
4288 /* copy/paste text 2 */
4289 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4290 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4291 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4292 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4293 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4294 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4295 ok (result != 0,
4296 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4297
4298 /* press char */
4299 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4300 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4301 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4302 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4303 ok (result != 0,
4304 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4305
4306 /* press del */
4307 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4308 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4309 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4310 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4311 ok (result != 0,
4312 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4313
4314 /* set char format */
4315 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4316 cf2.cbSize = sizeof(CHARFORMAT2A);
4317 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4318 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4319 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4320 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4321 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4322 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4323 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4324 ok (result != 0,
4325 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4326
4327 /* set para format */
4328 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4329 pf2.cbSize = sizeof(PARAFORMAT2);
4330 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4331 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4332 pf2.wAlignment = PFA_RIGHT;
4333 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4334 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4335 ok (result == 0,
4336 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4337
4338 /* EM_STREAM */
4339 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4340 es.dwCookie = (DWORD_PTR)&streamText;
4341 es.dwError = 0;
4342 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4343 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4344 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4345 ok (result != 0,
4346 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4347
4348 DestroyWindow(hwndRichEdit);
4349 }
4350
4351 struct exsetsel_s {
4352 LONG min;
4353 LONG max;
4354 LRESULT expected_retval;
4355 int expected_getsel_start;
4356 int expected_getsel_end;
4357 int _getsel_todo_wine;
4358 };
4359
4360 const struct exsetsel_s exsetsel_tests[] = {
4361 /* sanity tests */
4362 {5, 10, 10, 5, 10, 0},
4363 {15, 17, 17, 15, 17, 0},
4364 /* test cpMax > strlen() */
4365 {0, 100, 18, 0, 18, 1},
4366 /* test cpMin == cpMax */
4367 {5, 5, 5, 5, 5, 0},
4368 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4369 {-1, 0, 5, 5, 5, 0},
4370 {-1, 17, 5, 5, 5, 0},
4371 {-1, 18, 5, 5, 5, 0},
4372 /* test cpMin < 0 && cpMax < 0 */
4373 {-1, -1, 17, 17, 17, 0},
4374 {-4, -5, 17, 17, 17, 0},
4375 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4376 {0, -1, 18, 0, 18, 1},
4377 {17, -5, 18, 17, 18, 1},
4378 {18, -3, 17, 17, 17, 0},
4379 /* test if cpMin > cpMax */
4380 {15, 19, 18, 15, 18, 1},
4381 {19, 15, 18, 15, 18, 1}
4382 };
4383
4384 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4385 CHARRANGE cr;
4386 LRESULT result;
4387 int start, end;
4388
4389 cr.cpMin = setsel->min;
4390 cr.cpMax = setsel->max;
4391 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4392
4393 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4394
4395 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4396
4397 if (setsel->_getsel_todo_wine) {
4398 todo_wine {
4399 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);
4400 }
4401 } else {
4402 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);
4403 }
4404 }
4405
4406 static void test_EM_EXSETSEL(void)
4407 {
4408 HWND hwndRichEdit = new_richedit(NULL);
4409 int i;
4410 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4411
4412 /* sending some text to the window */
4413 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4414 /* 01234567890123456*/
4415 /* 10 */
4416
4417 for (i = 0; i < num_tests; i++) {
4418 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4419 }
4420
4421 DestroyWindow(hwndRichEdit);
4422 }
4423
4424 static void test_EM_REPLACESEL(int redraw)
4425 {
4426 HWND hwndRichEdit = new_richedit(NULL);
4427 char buffer[1024] = {0};
4428 int r;
4429 GETTEXTEX getText;
4430 CHARRANGE cr;
4431
4432 /* sending some text to the window */
4433 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4434 /* 01234567890123456*/
4435 /* 10 */
4436
4437 /* FIXME add more tests */
4438 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4439 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4440 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4441 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4442 r = strcmp(buffer, "testing");
4443 ok(0 == r, "expected %d, got %d\n", 0, r);
4444
4445 DestroyWindow(hwndRichEdit);
4446
4447 hwndRichEdit = new_richedit(NULL);
4448
4449 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4450 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4451
4452 /* Test behavior with carriage returns and newlines */
4453 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4454 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4455 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4456 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4457 r = strcmp(buffer, "RichEdit1");
4458 ok(0 == r, "expected %d, got %d\n", 0, r);
4459 getText.cb = 1024;
4460 getText.codepage = CP_ACP;
4461 getText.flags = GT_DEFAULT;
4462 getText.lpDefaultChar = NULL;
4463 getText.lpUsedDefChar = NULL;
4464 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4465 ok(strcmp(buffer, "RichEdit1") == 0,
4466 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4467
4468 /* Test number of lines reported after EM_REPLACESEL */
4469 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4470 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4471
4472 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4473 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4474 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4475 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4476 r = strcmp(buffer, "RichEdit1\r\n");
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 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4484 ok(strcmp(buffer, "RichEdit1\r") == 0,
4485 "EM_GETTEXTEX returned incorrect string\n");
4486
4487 /* Test number of lines reported after EM_REPLACESEL */
4488 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4489 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4490
4491 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4492 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4493 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4494
4495 /* Test number of lines reported after EM_REPLACESEL */
4496 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4497 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4498
4499 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4500 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4501 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4502 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4503
4504 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4505 r = strcmp(buffer, "RichEdit1\r\n");
4506 ok(0 == r, "expected %d, got %d\n", 0, r);
4507 getText.cb = 1024;
4508 getText.codepage = CP_ACP;
4509 getText.flags = GT_DEFAULT;
4510 getText.lpDefaultChar = NULL;
4511 getText.lpUsedDefChar = NULL;
4512 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4513 ok(strcmp(buffer, "RichEdit1\r") == 0,
4514 "EM_GETTEXTEX returned incorrect string\n");
4515
4516 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4517 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4518 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4519 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4520
4521 /* The following tests show that richedit should handle the special \r\r\n
4522 sequence by turning it into a single space on insertion. However,
4523 EM_REPLACESEL on WinXP returns the number of characters in the original
4524 string.
4525 */
4526
4527 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4528 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4529 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4530 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4531 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4532 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4533 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4534
4535 /* Test the actual string */
4536 getText.cb = 1024;
4537 getText.codepage = CP_ACP;
4538 getText.flags = GT_DEFAULT;
4539 getText.lpDefaultChar = NULL;
4540 getText.lpUsedDefChar = NULL;
4541 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4542 ok(strcmp(buffer, "\r\r") == 0,
4543 "EM_GETTEXTEX returned incorrect string\n");
4544
4545 /* Test number of lines reported after EM_REPLACESEL */
4546 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4547 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4548
4549 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4550 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4551 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4552 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4553 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4554 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4555 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4556
4557 /* Test the actual string */
4558 getText.cb = 1024;
4559 getText.codepage = CP_ACP;
4560 getText.flags = GT_DEFAULT;
4561 getText.lpDefaultChar = NULL;
4562 getText.lpUsedDefChar = NULL;
4563 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4564 ok(strcmp(buffer, " ") == 0,
4565 "EM_GETTEXTEX returned incorrect string\n");
4566
4567 /* Test number of lines reported after EM_REPLACESEL */
4568 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4569 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4570
4571 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4572 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4573 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4574 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4575 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4576 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4577 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4578
4579 /* Test the actual string */
4580 getText.cb = 1024;
4581 getText.codepage = CP_ACP;
4582 getText.flags = GT_DEFAULT;
4583 getText.lpDefaultChar = NULL;
4584 getText.lpUsedDefChar = NULL;
4585 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4586 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4587 "EM_GETTEXTEX returned incorrect string\n");
4588
4589 /* Test number of lines reported after EM_REPLACESEL */
4590 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4591 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4592
4593 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4594 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4595 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4596 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4597 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4598 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4599 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4600
4601 /* Test the actual string */
4602 getText.cb = 1024;
4603 getText.codepage = CP_ACP;
4604 getText.flags = GT_DEFAULT;
4605 getText.lpDefaultChar = NULL;
4606 getText.lpUsedDefChar = NULL;
4607 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4608 ok(strcmp(buffer, " \r") == 0,
4609 "EM_GETTEXTEX returned incorrect string\n");
4610
4611 /* Test number of lines reported after EM_REPLACESEL */
4612 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4613 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4614
4615 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4616 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4617 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4618 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4619 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4620 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4621 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4622
4623 /* Test the actual string */
4624 getText.cb = 1024;
4625 getText.codepage = CP_ACP;
4626 getText.flags = GT_DEFAULT;
4627 getText.lpDefaultChar = NULL;
4628 getText.lpUsedDefChar = NULL;
4629 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4630 ok(strcmp(buffer, " \r\r") == 0,
4631 "EM_GETTEXTEX returned incorrect string\n");
4632
4633 /* Test number of lines reported after EM_REPLACESEL */
4634 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4635 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4636
4637 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4638 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4639 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4640 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4641 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4642 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4643 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4644
4645 /* Test the actual string */
4646 getText.cb = 1024;
4647 getText.codepage = CP_ACP;
4648 getText.flags = GT_DEFAULT;
4649 getText.lpDefaultChar = NULL;
4650 getText.lpUsedDefChar = NULL;
4651 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4652 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4653 "EM_GETTEXTEX returned incorrect string\n");
4654
4655 /* Test number of lines reported after EM_REPLACESEL */
4656 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4657 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4658
4659 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4660 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4661 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4662 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4663 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4664 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4665 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4666
4667 /* Test the actual string */
4668 getText.cb = 1024;
4669 getText.codepage = CP_ACP;
4670 getText.flags = GT_DEFAULT;
4671 getText.lpDefaultChar = NULL;
4672 getText.lpUsedDefChar = NULL;
4673 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4674 ok(strcmp(buffer, "\r\r") == 0,
4675 "EM_GETTEXTEX returned incorrect string\n");
4676
4677 /* Test number of lines reported after EM_REPLACESEL */
4678 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4679 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4680
4681 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4682 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4683 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4684 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4685 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4686 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4687 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4688
4689 /* Test the actual string */
4690 getText.cb = 1024;
4691 getText.codepage = CP_ACP;
4692 getText.flags = GT_DEFAULT;
4693 getText.lpDefaultChar = NULL;
4694 getText.lpUsedDefChar = NULL;
4695 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4696 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4697 "EM_GETTEXTEX returned incorrect string\n");
4698
4699 /* Test number of lines reported after EM_REPLACESEL */
4700 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4701 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4702
4703 if (!redraw)
4704 /* This is needed to avoid interferring with keybd_event calls
4705 * on other tests that simulate keyboard events. */
4706 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4707
4708 DestroyWindow(hwndRichEdit);
4709 }
4710
4711 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4712 * to test the state of the modifiers (Ctrl/Alt/Shift).
4713 *
4714 * Therefore Ctrl-<key> keystrokes need to be simulated with
4715 * keybd_event or by using SetKeyboardState to set the modifiers
4716 * and SendMessage to simulate the keystrokes.
4717 */
4718 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4719 {
4720 LRESULT result;
4721 hold_key(VK_CONTROL);
4722 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
4723 release_key(VK_CONTROL);
4724 return result;
4725 }
4726
4727 static void test_WM_PASTE(void)
4728 {
4729 int result;
4730 char buffer[1024] = {0};
4731 const char* text1 = "testing paste\r";
4732 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4733 const char* text1_after = "testing paste\r\n";
4734 const char* text2 = "testing paste\r\rtesting paste";
4735 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4736 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4737 HWND hwndRichEdit = new_richedit(NULL);
4738
4739 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4740 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
4741
4742 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4743 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4744 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4745 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4746 /* Pasted text should be visible at this step */
4747 result = strcmp(text1_step1, buffer);
4748 ok(result == 0,
4749 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4750
4751 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4752 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4753 /* Text should be the same as before (except for \r -> \r\n conversion) */
4754 result = strcmp(text1_after, buffer);
4755 ok(result == 0,
4756 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4757
4758 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
4759 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
4760 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4761 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4762 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4763 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4764 /* Pasted text should be visible at this step */
4765 result = strcmp(text3, buffer);
4766 ok(result == 0,
4767 "test paste: strcmp = %i\n", result);
4768 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4769 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4770 /* Text should be the same as before (except for \r -> \r\n conversion) */
4771 result = strcmp(text2_after, buffer);
4772 ok(result == 0,
4773 "test paste: strcmp = %i\n", result);
4774 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4775 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4776 /* Text should revert to post-paste state */
4777 result = strcmp(buffer,text3);
4778 ok(result == 0,
4779 "test paste: strcmp = %i\n", result);
4780
4781 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4782 /* Send WM_CHAR to simulate Ctrl-V */
4783 SendMessageA(hwndRichEdit, WM_CHAR, 22,
4784 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4785 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4786 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4787 result = strcmp(buffer,"");
4788 ok(result == 0,
4789 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4790
4791 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4792 * with SetKeyboard state. */
4793
4794 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4795 /* Simulates paste (Ctrl-V) */
4796 hold_key(VK_CONTROL);
4797 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
4798 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4799 release_key(VK_CONTROL);
4800 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4801 result = strcmp(buffer,"paste");
4802 ok(result == 0,
4803 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4804
4805 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4806 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
4807 /* Simulates copy (Ctrl-C) */
4808 hold_key(VK_CONTROL);
4809 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
4810 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
4811 release_key(VK_CONTROL);
4812 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4813 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4814 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4815 result = strcmp(buffer,"testing");
4816 ok(result == 0,
4817 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4818
4819 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4820 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
4821 /* Simulates select all (Ctrl-A) */
4822 hold_key(VK_CONTROL);
4823 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
4824 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
4825 /* Simulates select cut (Ctrl-X) */
4826 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
4827 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
4828 release_key(VK_CONTROL);
4829 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4830 result = strcmp(buffer,"");
4831 ok(result == 0,
4832 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4833 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4834 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4835 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4836 result = strcmp(buffer,"cut\r\n");
4837 todo_wine ok(result == 0,
4838 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4839 /* Simulates undo (Ctrl-Z) */
4840 hold_key(VK_CONTROL);
4841 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
4842 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4843 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4844 result = strcmp(buffer,"");
4845 ok(result == 0,
4846 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4847 /* Simulates redo (Ctrl-Y) */
4848 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
4849 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4850 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4851 result = strcmp(buffer,"cut\r\n");
4852 todo_wine ok(result == 0,
4853 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4854 release_key(VK_CONTROL);
4855
4856 DestroyWindow(hwndRichEdit);
4857 }
4858
4859 static void test_EM_FORMATRANGE(void)
4860 {
4861 int r, i, tpp_x, tpp_y;
4862 HDC hdc;
4863 HWND hwndRichEdit = new_richedit(NULL);
4864 FORMATRANGE fr;
4865 BOOL skip_non_english;
4866 static const struct {
4867 const char *string; /* The string */
4868 int first; /* First 'pagebreak', 0 for don't care */
4869 int second; /* Second 'pagebreak', 0 for don't care */
4870 } fmtstrings[] = {
4871 {"WINE wine", 0, 0},
4872 {"WINE wineWine", 0, 0},
4873 {"WINE\r\nwine\r\nwine", 5, 10},
4874 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4875 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4876 };
4877
4878 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4879 if (skip_non_english)
4880 skip("Skipping some tests on non-English platform\n");
4881
4882 hdc = GetDC(hwndRichEdit);
4883 ok(hdc != NULL, "Could not get HDC\n");
4884
4885 /* Calculate the twips per pixel */
4886 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4887 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4888
4889 /* Test the simple case where all the text fits in the page rect. */
4890 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4891 fr.hdc = fr.hdcTarget = hdc;
4892 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4893 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4894 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4895 fr.chrg.cpMin = 0;
4896 fr.chrg.cpMax = -1;
4897 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4898 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4899
4900 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4901 fr.rc.bottom = fr.rcPage.bottom;
4902 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4903 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4904
4905 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4906
4907 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4908 {
4909 GETTEXTLENGTHEX gtl;
4910 SIZE stringsize;
4911 int len;
4912
4913 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
4914
4915 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4916 gtl.codepage = CP_ACP;
4917 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4918
4919 /* Get some size information for the string */
4920 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4921
4922 /* Define the box to be half the width needed and a bit larger than the height.
4923 * Changes to the width means we have at least 2 pages. Changes to the height
4924 * is done so we can check the changing of fr.rc.bottom.
4925 */
4926 fr.hdc = fr.hdcTarget = hdc;
4927 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4928 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4929 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4930
4931 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4932 todo_wine {
4933 ok(r == len, "Expected %d, got %d\n", len, r);
4934 }
4935
4936 /* We know that the page can't hold the full string. See how many characters
4937 * are on the first one
4938 */
4939 fr.chrg.cpMin = 0;
4940 fr.chrg.cpMax = -1;
4941 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
4942 todo_wine {
4943 if (! skip_non_english)
4944 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4945 }
4946 if (fmtstrings[i].first)
4947 todo_wine {
4948 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4949 }
4950 else
4951 ok(r < len, "Expected < %d, got %d\n", len, r);
4952
4953 /* Do another page */
4954 fr.chrg.cpMin = r;
4955 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
4956 if (fmtstrings[i].second)
4957 todo_wine {
4958 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4959 }
4960 else if (! skip_non_english)
4961 ok (r < len, "Expected < %d, got %d\n", len, r);
4962
4963 /* There is at least on more page, but we don't care */
4964
4965 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4966 todo_wine {
4967 ok(r == len, "Expected %d, got %d\n", len, r);
4968 }
4969 }
4970
4971 ReleaseDC(NULL, hdc);
4972 DestroyWindow(hwndRichEdit);
4973 }
4974
4975 static int nCallbackCount = 0;
4976
4977 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4978 LONG cb, LONG* pcb)
4979 {
4980 const char text[] = {'t','e','s','t'};
4981
4982 if (sizeof(text) <= cb)
4983 {
4984 if ((int)dwCookie != nCallbackCount)
4985 {
4986 *pcb = 0;
4987 return 0;
4988 }
4989
4990 memcpy (pbBuff, text, sizeof(text));
4991 *pcb = sizeof(text);
4992
4993 nCallbackCount++;
4994
4995 return 0;
4996 }
4997 else
4998 return 1; /* indicates callback failed */
4999 }
5000
5001 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5002 LPBYTE pbBuff,
5003 LONG cb,
5004 LONG *pcb)
5005 {
5006 const char** str = (const char**)dwCookie;
5007 int size = strlen(*str);
5008 *pcb = cb;
5009 if (*pcb > size) {
5010 *pcb = size;
5011 }
5012 if (*pcb > 0) {
5013 memcpy(pbBuff, *str, *pcb);
5014 *str += *pcb;
5015 }
5016 return 0;
5017 }
5018
5019 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5020 LPBYTE pbBuff,
5021 LONG cb,
5022 LONG *pcb)
5023 {
5024 DWORD *phase = (DWORD *)dwCookie;
5025
5026 if(*phase == 0){
5027 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5028 *pcb = sizeof(first) - 1;
5029 memcpy(pbBuff, first, *pcb);
5030 }else if(*phase == 1){
5031 static const char second[] = "\x8f\xc3\x8b";
5032 *pcb = sizeof(second) - 1;
5033 memcpy(pbBuff, second, *pcb);
5034 }else
5035 *pcb = 0;
5036
5037 ++*phase;
5038
5039 return 0;
5040 }
5041
5042 struct StringWithLength {
5043 int length;
5044 char *buffer;
5045 };
5046
5047 /* This callback is used to handled the null characters in a string. */
5048 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5049 LPBYTE pbBuff,
5050 LONG cb,
5051 LONG *pcb)
5052 {
5053 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5054 int size = str->length;
5055 *pcb = cb;
5056 if (*pcb > size) {
5057 *pcb = size;
5058 }
5059 if (*pcb > 0) {
5060 memcpy(pbBuff, str->buffer, *pcb);
5061 str->buffer += *pcb;
5062 str->length -= *pcb;
5063 }
5064 return 0;
5065 }
5066
5067 static void test_EM_STREAMIN(void)
5068 {
5069 HWND hwndRichEdit = new_richedit(NULL);
5070 DWORD phase;
5071 LRESULT result;
5072 EDITSTREAM es;
5073 char buffer[1024] = {0};
5074
5075 const char * streamText0 = "{\\rtf1 TestSomeText}";
5076 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5077 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5078
5079 const char * streamText1 =
5080 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5081 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5082 "}\r\n";
5083
5084 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5085 const char * streamText2 =
5086 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5087 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5088 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5089 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5090 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5091 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5092 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5093
5094 const char * streamText3 = "RichEdit1";
5095
5096 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5097
5098 const char * streamText4 =
5099 "This text just needs to be long enough to cause run to be split onto "
5100 "two separate lines and make sure the null terminating character is "
5101 "handled properly.\0";
5102 int length4 = strlen(streamText4) + 1;
5103 struct StringWithLength cookieForStream4 = {
5104 length4,
5105 (char *)streamText4,
5106 };
5107
5108 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5109 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5110 struct StringWithLength cookieForStream5 = {
5111 sizeof(streamText5),
5112 (char *)streamText5,
5113 };
5114
5115 /* Minimal test without \par at the end */
5116 es.dwCookie = (DWORD_PTR)&streamText0;
5117 es.dwError = 0;
5118 es.pfnCallback = test_EM_STREAMIN_esCallback;
5119 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5120 ok(result == 12, "got %ld, expected %d\n", result, 12);
5121
5122 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5123 ok (result == 12,
5124 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5125 result = strcmp (buffer,"TestSomeText");
5126 ok (result == 0,
5127 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5128 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5129
5130 /* Native richedit 2.0 ignores last \par */
5131 es.dwCookie = (DWORD_PTR)&streamText0a;
5132 es.dwError = 0;
5133 es.pfnCallback = test_EM_STREAMIN_esCallback;
5134 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5135 ok(result == 12, "got %ld, expected %d\n", result, 12);
5136
5137 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5138 ok (result == 12,
5139 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5140 result = strcmp (buffer,"TestSomeText");
5141 ok (result == 0,
5142 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5143 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5144
5145 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5146 es.dwCookie = (DWORD_PTR)&streamText0b;
5147 es.dwError = 0;
5148 es.pfnCallback = test_EM_STREAMIN_esCallback;
5149 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5150 ok(result == 13, "got %ld, expected %d\n", result, 13);
5151
5152 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5153 ok (result == 14,
5154 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5155 result = strcmp (buffer,"TestSomeText\r\n");
5156 ok (result == 0,
5157 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5158 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5159
5160 es.dwCookie = (DWORD_PTR)&streamText1;
5161 es.dwError = 0;
5162 es.pfnCallback = test_EM_STREAMIN_esCallback;
5163 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5164 ok(result == 12, "got %ld, expected %d\n", result, 12);
5165
5166 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5167 ok (result == 12,
5168 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5169 result = strcmp (buffer,"TestSomeText");
5170 ok (result == 0,
5171 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5172 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5173
5174 es.dwCookie = (DWORD_PTR)&streamText2;
5175 es.dwError = 0;
5176 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5177 ok(result == 0, "got %ld, expected %d\n", result, 0);
5178
5179 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5180 ok (result == 0,
5181 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5182 ok (strlen(buffer) == 0,
5183 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5184 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5185
5186 es.dwCookie = (DWORD_PTR)&streamText3;
5187 es.dwError = 0;
5188 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5189 ok(result == 0, "got %ld, expected %d\n", result, 0);
5190
5191 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5192 ok (result == 0,
5193 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5194 ok (strlen(buffer) == 0,
5195 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5196 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5197
5198 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5199 es.dwError = 0;
5200 es.pfnCallback = test_EM_STREAMIN_esCallback;
5201 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5202 ok(result == 18, "got %ld, expected %d\n", result, 18);
5203
5204 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5205 ok(result == 15,
5206 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5207 result = strcmp (buffer,"TestUTF8WithBOM");
5208 ok(result == 0,
5209 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5210 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5211
5212 phase = 0;
5213 es.dwCookie = (DWORD_PTR)&phase;
5214 es.dwError = 0;
5215 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5216 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5217 ok(result == 8, "got %ld\n", result);
5218
5219 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5220 ok(result == 3,
5221 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5222 result = memcmp (buffer,"\xd6\xcf\xcb", 3);
5223 ok(result == 0,
5224 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5225 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5226
5227 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5228 es.dwError = 0;
5229 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5230 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5231 ok(result == length4, "got %ld, expected %d\n", result, length4);
5232
5233 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5234 ok (result == length4,
5235 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5236 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5237
5238 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5239 es.dwError = 0;
5240 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5241 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5242 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5243
5244 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5245 ok (result == length5,
5246 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5247 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5248
5249 DestroyWindow(hwndRichEdit);
5250 }
5251
5252 static void test_EM_StreamIn_Undo(void)
5253 {
5254 /* The purpose of this test is to determine when a EM_StreamIn should be
5255 * undoable. This is important because WM_PASTE currently uses StreamIn and
5256 * pasting should always be undoable but streaming isn't always.
5257 *
5258 * cases to test:
5259 * StreamIn plain text without SFF_SELECTION.
5260 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5261 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5262 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5263 * Feel free to add tests for other text modes or StreamIn things.
5264 */
5265
5266
5267 HWND hwndRichEdit = new_richedit(NULL);
5268 LRESULT result;
5269 EDITSTREAM es;
5270 char buffer[1024] = {0};
5271 const char randomtext[] = "Some text";
5272
5273 es.pfnCallback = EditStreamCallback;
5274
5275 /* StreamIn, no SFF_SELECTION */
5276 es.dwCookie = nCallbackCount;
5277 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5278 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5279 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5280 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5281 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5282 result = strcmp (buffer,"test");
5283 ok (result == 0,
5284 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5285
5286 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5287 ok (result == FALSE,
5288 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5289
5290 /* StreamIn, SFF_SELECTION, but nothing selected */
5291 es.dwCookie = nCallbackCount;
5292 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5293 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5294 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5295 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5296 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5297 result = strcmp (buffer,"testSome text");
5298 ok (result == 0,
5299 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5300
5301 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5302 ok (result == TRUE,
5303 "EM_STREAMIN with SFF_SELECTION but no selection set "
5304 "should create an undo\n");
5305
5306 /* StreamIn, SFF_SELECTION, with a selection */
5307 es.dwCookie = nCallbackCount;
5308 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5309 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5310 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5311 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5312 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5313 result = strcmp (buffer,"Sometesttext");
5314 ok (result == 0,
5315 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5316
5317 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5318 ok (result == TRUE,
5319 "EM_STREAMIN with SFF_SELECTION and selection set "
5320 "should create an undo\n");
5321
5322 DestroyWindow(hwndRichEdit);
5323 }
5324
5325 static BOOL is_em_settextex_supported(HWND hwnd)
5326 {
5327 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5328 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5329 }
5330
5331 static void test_unicode_conversions(void)
5332 {
5333 static const WCHAR tW[] = {'t',0};
5334 static const WCHAR teW[] = {'t','e',0};
5335 static const WCHAR textW[] = {'t','e','s','t',0};
5336 static const char textA[] = "test";
5337 char bufA[64];
5338 WCHAR bufW[64];
5339 HWND hwnd;
5340 int em_settextex_supported, ret;
5341
5342 #define set_textA(hwnd, wm_set_text, txt) \
5343 do { \
5344 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5345 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5346 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5347 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5348 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5349 } while(0)
5350 #define expect_textA(hwnd, wm_get_text, txt) \
5351 do { \
5352 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5353 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5354 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5355 memset(bufA, 0xAA, sizeof(bufA)); \
5356 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5357 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5358 ret = lstrcmpA(bufA, txt); \
5359 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5360 } while(0)
5361
5362 #define set_textW(hwnd, wm_set_text, txt) \
5363 do { \
5364 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5365 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5366 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5367 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5368 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5369 } while(0)
5370 #define expect_textW(hwnd, wm_get_text, txt) \
5371 do { \
5372 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5373 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5374 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5375 memset(bufW, 0xAA, sizeof(bufW)); \
5376 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5377 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5378 ret = lstrcmpW(bufW, txt); \
5379 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5380 } while(0)
5381 #define expect_empty(hwnd, wm_get_text) \
5382 do { \
5383 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5384 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5385 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5386 memset(bufA, 0xAA, sizeof(bufA)); \
5387 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5388 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5389 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5390 } while(0)
5391
5392 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5393 0, 0, 200, 60, 0, 0, 0, 0);
5394 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5395
5396 ret = IsWindowUnicode(hwnd);
5397 ok(ret, "RichEdit20W should be unicode under NT\n");
5398
5399 /* EM_SETTEXTEX is supported starting from version 3.0 */
5400 em_settextex_supported = is_em_settextex_supported(hwnd);
5401 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5402 em_settextex_supported ? "" : "NOT ");
5403
5404 expect_empty(hwnd, WM_GETTEXT);
5405 expect_empty(hwnd, EM_GETTEXTEX);
5406
5407 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5408 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5409 expect_textA(hwnd, WM_GETTEXT, "t");
5410 expect_textA(hwnd, EM_GETTEXTEX, "t");
5411 expect_textW(hwnd, EM_GETTEXTEX, tW);
5412
5413 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5414 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5415 expect_textA(hwnd, WM_GETTEXT, "te");
5416 expect_textA(hwnd, EM_GETTEXTEX, "te");
5417 expect_textW(hwnd, EM_GETTEXTEX, teW);
5418
5419 set_textA(hwnd, WM_SETTEXT, NULL);
5420 expect_empty(hwnd, WM_GETTEXT);
5421 expect_empty(hwnd, EM_GETTEXTEX);
5422
5423 set_textA(hwnd, WM_SETTEXT, textA);
5424 expect_textA(hwnd, WM_GETTEXT, textA);
5425 expect_textA(hwnd, EM_GETTEXTEX, textA);
5426 expect_textW(hwnd, EM_GETTEXTEX, textW);
5427
5428 if (em_settextex_supported)
5429 {
5430 set_textA(hwnd, EM_SETTEXTEX, textA);
5431 expect_textA(hwnd, WM_GETTEXT, textA);
5432 expect_textA(hwnd, EM_GETTEXTEX, textA);
5433 expect_textW(hwnd, EM_GETTEXTEX, textW);
5434 }
5435
5436 set_textW(hwnd, WM_SETTEXT, textW);
5437 expect_textW(hwnd, WM_GETTEXT, textW);
5438 expect_textA(hwnd, WM_GETTEXT, textA);
5439 expect_textW(hwnd, EM_GETTEXTEX, textW);
5440 expect_textA(hwnd, EM_GETTEXTEX, textA);
5441
5442 if (em_settextex_supported)
5443 {
5444 set_textW(hwnd, EM_SETTEXTEX, textW);
5445 expect_textW(hwnd, WM_GETTEXT, textW);
5446 expect_textA(hwnd, WM_GETTEXT, textA);
5447 expect_textW(hwnd, EM_GETTEXTEX, textW);
5448 expect_textA(hwnd, EM_GETTEXTEX, textA);
5449 }
5450 DestroyWindow(hwnd);
5451
5452 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5453 0, 0, 200, 60, 0, 0, 0, 0);
5454 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5455
5456 ret = IsWindowUnicode(hwnd);
5457 ok(!ret, "RichEdit20A should NOT be unicode\n");
5458
5459 set_textA(hwnd, WM_SETTEXT, textA);
5460 expect_textA(hwnd, WM_GETTEXT, textA);
5461 expect_textA(hwnd, EM_GETTEXTEX, textA);
5462 expect_textW(hwnd, EM_GETTEXTEX, textW);
5463
5464 if (em_settextex_supported)
5465 {
5466 set_textA(hwnd, EM_SETTEXTEX, textA);
5467 expect_textA(hwnd, WM_GETTEXT, textA);
5468 expect_textA(hwnd, EM_GETTEXTEX, textA);
5469 expect_textW(hwnd, EM_GETTEXTEX, textW);
5470 }
5471
5472 set_textW(hwnd, WM_SETTEXT, textW);
5473 expect_textW(hwnd, WM_GETTEXT, textW);
5474 expect_textA(hwnd, WM_GETTEXT, textA);
5475 expect_textW(hwnd, EM_GETTEXTEX, textW);
5476 expect_textA(hwnd, EM_GETTEXTEX, textA);
5477
5478 if (em_settextex_supported)
5479 {
5480 set_textW(hwnd, EM_SETTEXTEX, textW);
5481 expect_textW(hwnd, WM_GETTEXT, textW);
5482 expect_textA(hwnd, WM_GETTEXT, textA);
5483 expect_textW(hwnd, EM_GETTEXTEX, textW);
5484 expect_textA(hwnd, EM_GETTEXTEX, textA);
5485 }
5486 DestroyWindow(hwnd);
5487 }
5488
5489 static void test_WM_CHAR(void)
5490 {
5491 HWND hwnd;
5492 int ret;
5493 const char * char_list = "abc\rabc\r";
5494 const char * expected_content_single = "abcabc";
5495 const char * expected_content_multi = "abc\r\nabc\r\n";
5496 char buffer[64] = {0};
5497 const char * p;
5498
5499 /* single-line control must IGNORE carriage returns */
5500 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5501 0, 0, 200, 60, 0, 0, 0, 0);
5502 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5503
5504 p = char_list;
5505 while (*p != '\0') {
5506 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5507 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5508 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5509 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5510 p++;
5511 }
5512
5513 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5514 ret = strcmp(buffer, expected_content_single);
5515 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5516
5517 DestroyWindow(hwnd);
5518
5519 /* multi-line control inserts CR normally */
5520 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5521 0, 0, 200, 60, 0, 0, 0, 0);
5522 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5523
5524 p = char_list;
5525 while (*p != '\0') {
5526 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5527 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5528 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5529 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5530 p++;
5531 }
5532
5533 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5534 ret = strcmp(buffer, expected_content_multi);
5535 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5536
5537 DestroyWindow(hwnd);
5538 }
5539
5540 static void test_EM_GETTEXTLENGTHEX(void)
5541 {
5542 HWND hwnd;
5543 GETTEXTLENGTHEX gtl;
5544 int ret;
5545 const char * base_string = "base string";
5546 const char * test_string = "a\nb\n\n\r\n";
5547 const char * test_string_after = "a";
5548 const char * test_string_2 = "a\rtest\rstring";
5549 char buffer[64] = {0};
5550
5551 /* single line */
5552 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5553 0, 0, 200, 60, 0, 0, 0, 0);
5554 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5555
5556 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5557 gtl.codepage = CP_ACP;
5558 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5559 ok(ret == 0, "ret %d\n",ret);
5560
5561 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5562 gtl.codepage = CP_ACP;
5563 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5564 ok(ret == 0, "ret %d\n",ret);
5565
5566 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5567
5568 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5569 gtl.codepage = CP_ACP;
5570 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5571 ok(ret == strlen(base_string), "ret %d\n",ret);
5572
5573 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5574 gtl.codepage = CP_ACP;
5575 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5576 ok(ret == strlen(base_string), "ret %d\n",ret);
5577
5578 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5579
5580 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5581 gtl.codepage = CP_ACP;
5582 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5583 ok(ret == 1, "ret %d\n",ret);
5584
5585 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5586 gtl.codepage = CP_ACP;
5587 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5588 ok(ret == 1, "ret %d\n",ret);
5589
5590 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5591 ret = strcmp(buffer, test_string_after);
5592 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5593
5594 DestroyWindow(hwnd);
5595
5596 /* multi line */
5597 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5598 0, 0, 200, 60, 0, 0, 0, 0);
5599 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5600
5601 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5602 gtl.codepage = CP_ACP;
5603 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5604 ok(ret == 0, "ret %d\n",ret);
5605
5606 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5607 gtl.codepage = CP_ACP;
5608 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5609 ok(ret == 0, "ret %d\n",ret);
5610
5611 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5612
5613 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5614 gtl.codepage = CP_ACP;
5615 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5616 ok(ret == strlen(base_string), "ret %d\n",ret);
5617
5618 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5619 gtl.codepage = CP_ACP;
5620 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5621 ok(ret == strlen(base_string), "ret %d\n",ret);
5622
5623 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5624
5625 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5626 gtl.codepage = CP_ACP;
5627 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5628 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5629
5630 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5631 gtl.codepage = CP_ACP;
5632 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5633 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5634
5635 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5636
5637 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5638 gtl.codepage = CP_ACP;
5639 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5640 ok(ret == 10, "ret %d\n",ret);
5641
5642 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5643 gtl.codepage = CP_ACP;
5644 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5645 ok(ret == 6, "ret %d\n",ret);
5646
5647 /* Unicode/NUMCHARS/NUMBYTES */
5648 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5649
5650 gtl.flags = GTL_DEFAULT;
5651 gtl.codepage = 1200;
5652 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5653 ok(ret == lstrlenA(test_string_2),
5654 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5655
5656 gtl.flags = GTL_NUMCHARS;
5657 gtl.codepage = 1200;
5658 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5659 ok(ret == lstrlenA(test_string_2),
5660 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5661
5662 gtl.flags = GTL_NUMBYTES;
5663 gtl.codepage = 1200;
5664 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5665 ok(ret == lstrlenA(test_string_2)*2,
5666 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5667
5668 gtl.flags = GTL_PRECISE;
5669 gtl.codepage = 1200;
5670 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5671 ok(ret == lstrlenA(test_string_2)*2,
5672 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5673
5674 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5675 gtl.codepage = 1200;
5676 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5677 ok(ret == lstrlenA(test_string_2),
5678 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5679
5680 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5681 gtl.codepage = 1200;
5682 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5683 ok(ret == E_INVALIDARG,
5684 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5685
5686 DestroyWindow(hwnd);
5687 }
5688
5689
5690 /* globals that parent and child access when checking event masks & notifications */
5691 static HWND eventMaskEditHwnd = 0;
5692 static int queriedEventMask;
5693 static int watchForEventMask = 0;
5694
5695 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5696 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5697 {
5698 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5699 {
5700 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5701 }
5702 return DefWindowProcA(hwnd, message, wParam, lParam);
5703 }
5704
5705 /* test event masks in combination with WM_COMMAND */
5706 static void test_eventMask(void)
5707 {
5708 HWND parent;
5709 int ret, style;
5710 WNDCLASSA cls;
5711 const char text[] = "foo bar\n";
5712 int eventMask;
5713
5714 /* register class to capture WM_COMMAND */
5715 cls.style = 0;
5716 cls.lpfnWndProc = ParentMsgCheckProcA;
5717 cls.cbClsExtra = 0;
5718 cls.cbWndExtra = 0;
5719 cls.hInstance = GetModuleHandleA(0);
5720 cls.hIcon = 0;
5721 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5722 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5723 cls.lpszMenuName = NULL;
5724 cls.lpszClassName = "EventMaskParentClass";
5725 if(!RegisterClassA(&cls)) assert(0);
5726
5727 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5728 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5729 ok (parent != 0, "Failed to create parent window\n");
5730
5731 eventMaskEditHwnd = new_richedit(parent);
5732 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5733
5734 eventMask = ENM_CHANGE | ENM_UPDATE;
5735 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5736 ok(ret == ENM_NONE, "wrong event mask\n");
5737 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5738 ok(ret == eventMask, "failed to set event mask\n");
5739
5740 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5741 queriedEventMask = 0; /* initialize to something other than we expect */
5742 watchForEventMask = EN_CHANGE;
5743 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
5744 ok(ret == TRUE, "failed to set text\n");
5745 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5746 notification in response to WM_SETTEXT */
5747 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5748 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5749
5750 /* check to see if EN_CHANGE is sent when redraw is turned off */
5751 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5752 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5753 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5754 /* redraw is disabled by making the window invisible. */
5755 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5756 queriedEventMask = 0; /* initialize to something other than we expect */
5757 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5758 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5759 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5760 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5761 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5762
5763 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5764 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5765 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
5766 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5767 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5768 watchForEventMask = EN_UPDATE;
5769 queriedEventMask = 0; /* initialize to something other than we expect */
5770 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5771 ok(queriedEventMask == 0,
5772 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5773 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
5774 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5775 queriedEventMask = 0; /* initialize to something other than we expect */
5776 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5777 ok(queriedEventMask == eventMask,
5778 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5779
5780
5781 DestroyWindow(parent);
5782 }
5783
5784 static int received_WM_NOTIFY = 0;
5785 static int modify_at_WM_NOTIFY = 0;
5786 static BOOL filter_on_WM_NOTIFY = FALSE;
5787 static HWND hwndRichedit_WM_NOTIFY;
5788
5789 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5790 {
5791 if(message == WM_NOTIFY)
5792 {
5793 received_WM_NOTIFY = 1;
5794 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5795 if (filter_on_WM_NOTIFY) return TRUE;
5796 }
5797 return DefWindowProcA(hwnd, message, wParam, lParam);
5798 }
5799
5800 static void test_WM_NOTIFY(void)
5801 {
5802 HWND parent;
5803 WNDCLASSA cls;
5804 CHARFORMAT2A cf2;
5805 int sel_start, sel_end;
5806
5807 /* register class to capture WM_NOTIFY */
5808 cls.style = 0;
5809 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5810 cls.cbClsExtra = 0;
5811 cls.cbWndExtra = 0;
5812 cls.hInstance = GetModuleHandleA(0);
5813 cls.hIcon = 0;
5814 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5815 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5816 cls.lpszMenuName = NULL;
5817 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5818 if(!RegisterClassA(&cls)) assert(0);
5819
5820 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5821 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5822 ok (parent != 0, "Failed to create parent window\n");
5823
5824 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5825 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5826
5827 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5828
5829 /* Notifications for selection change should only be sent when selection
5830 actually changes. EM_SETCHARFORMAT is one message that calls
5831 ME_CommitUndo, which should check whether message should be sent */
5832 received_WM_NOTIFY = 0;
5833 cf2.cbSize = sizeof(CHARFORMAT2A);
5834 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5835 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5836 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5837 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
5838 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5839
5840 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5841 already at 0. */
5842 received_WM_NOTIFY = 0;
5843 modify_at_WM_NOTIFY = 0;
5844 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5845 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5846 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5847
5848 received_WM_NOTIFY = 0;
5849 modify_at_WM_NOTIFY = 0;
5850 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5851 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5852
5853 received_WM_NOTIFY = 0;
5854 modify_at_WM_NOTIFY = 0;
5855 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5856 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5857 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5858
5859 /* Test for WM_NOTIFY messages with redraw disabled. */
5860 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5861 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5862 received_WM_NOTIFY = 0;
5863 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5864 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5865 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5866
5867 /* Test filtering key events. */
5868 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5869 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5870 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5871 received_WM_NOTIFY = 0;
5872 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5873 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5874 ok(sel_start == 1 && sel_end == 1,
5875 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5876 filter_on_WM_NOTIFY = TRUE;
5877 received_WM_NOTIFY = 0;
5878 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5879 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5880 ok(sel_start == 1 && sel_end == 1,
5881 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5882
5883 /* test with owner set to NULL */
5884 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5885 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5886 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5887 ok(sel_start == 1 && sel_end == 1,
5888 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5889
5890 DestroyWindow(hwndRichedit_WM_NOTIFY);
5891 DestroyWindow(parent);
5892 }
5893
5894 static void test_undo_coalescing(void)
5895 {
5896 HWND hwnd;
5897 int result;
5898 char buffer[64] = {0};
5899
5900 /* multi-line control inserts CR normally */
5901 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5902 0, 0, 200, 60, 0, 0, 0, 0);
5903 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5904
5905 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
5906 ok (result == FALSE, "Can undo after window creation.\n");
5907 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5908 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5909 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
5910 ok (result == FALSE, "Can redo after window creation.\n");
5911 result = SendMessageA(hwnd, EM_REDO, 0, 0);
5912 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5913
5914 /* Test the effect of arrows keys during typing on undo transactions*/
5915 simulate_typing_characters(hwnd, "one two three");
5916 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5917 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
5918 simulate_typing_characters(hwnd, " four five six");
5919
5920 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
5921 ok (result == FALSE, "Can redo before anything is undone.\n");
5922 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
5923 ok (result == TRUE, "Cannot undo typed characters.\n");
5924 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5925 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5926 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
5927 ok (result == TRUE, "Cannot redo after undo.\n");
5928 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5929 result = strcmp(buffer, "one two three");
5930 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5931
5932 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
5933 ok (result == TRUE, "Cannot undo typed characters.\n");
5934 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
5935 ok (result == TRUE, "Failed to undo typed characters.\n");
5936 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5937 result = strcmp(buffer, "");
5938 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5939
5940 /* Test the effect of focus changes during typing on undo transactions*/
5941 simulate_typing_characters(hwnd, "one two three");
5942 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
5943 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5944 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
5945 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
5946 simulate_typing_characters(hwnd, " four five six");
5947 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5948 ok (result == TRUE, "Failed to undo typed characters.\n");
5949 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5950 result = strcmp(buffer, "one two three");
5951 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5952
5953 /* Test the effect of the back key during typing on undo transactions */
5954 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5955 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5956 ok (result == TRUE, "Failed to clear the text.\n");
5957 simulate_typing_characters(hwnd, "one two threa");
5958 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
5959 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5960 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
5961 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
5962 simulate_typing_characters(hwnd, "e four five six");
5963 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5964 ok (result == TRUE, "Failed to undo typed characters.\n");
5965 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5966 result = strcmp(buffer, "");
5967 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5968
5969 /* Test the effect of the delete key during typing on undo transactions */
5970 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5971 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5972 ok(result == TRUE, "Failed to set the text.\n");
5973 SendMessageA(hwnd, EM_SETSEL, 1, 1);
5974 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5975 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
5976 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5977 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
5978 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5979 ok (result == TRUE, "Failed to undo typed characters.\n");
5980 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5981 result = strcmp(buffer, "acd");
5982 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5983 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
5984 ok (result == TRUE, "Failed to undo typed characters.\n");
5985 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5986 result = strcmp(buffer, "abcd");
5987 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5988
5989 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5990 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5991 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5992 ok (result == TRUE, "Failed to clear the text.\n");
5993 simulate_typing_characters(hwnd, "one two three");
5994 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
5995 ok (result == 0, "expected %d but got %d\n", 0, result);
5996 simulate_typing_characters(hwnd, " four five six");
5997 result = SendMessageA(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, "one two three");
6001 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6002 result = SendMessageA(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, "");
6006 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6007
6008 DestroyWindow(hwnd);
6009 }
6010
6011 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6012 {
6013 int length;
6014
6015 /* MSDN lied, length is actually the number of bytes. */
6016 length = bytes / sizeof(WCHAR);
6017 switch(code)
6018 {
6019 case WB_ISDELIMITER:
6020 return text[pos] == 'X';
6021 case WB_LEFT:
6022 case WB_MOVEWORDLEFT:
6023 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6024 return pos-1;
6025 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6026 case WB_LEFTBREAK:
6027 pos--;
6028 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6029 pos--;
6030 return pos;
6031 case WB_RIGHT:
6032 case WB_MOVEWORDRIGHT:
6033 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6034 return pos+1;
6035 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6036 case WB_RIGHTBREAK:
6037 pos++;
6038 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6039 pos++;
6040 return pos;
6041 default:
6042 ok(FALSE, "Unexpected code %d\n", code);
6043 break;
6044 }
6045 return 0;
6046 }
6047
6048 static void test_word_movement(void)
6049 {
6050 HWND hwnd;
6051 int result;
6052 int sel_start, sel_end;
6053 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6054
6055 /* multi-line control inserts CR normally */
6056 hwnd = new_richedit(NULL);
6057
6058 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6059 ok (result == TRUE, "Failed to clear the text.\n");
6060 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6061 /* |one two three */
6062
6063 send_ctrl_key(hwnd, VK_RIGHT);
6064 /* one |two three */
6065 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6066 ok(sel_start == sel_end, "Selection should be empty\n");
6067 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6068
6069 send_ctrl_key(hwnd, VK_RIGHT);
6070 /* one two |three */
6071 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6072 ok(sel_start == sel_end, "Selection should be empty\n");
6073 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6074
6075 send_ctrl_key(hwnd, VK_LEFT);
6076 /* one |two three */
6077 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6078 ok(sel_start == sel_end, "Selection should be empty\n");
6079 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6080
6081 send_ctrl_key(hwnd, VK_LEFT);
6082 /* |one two three */
6083 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6084 ok(sel_start == sel_end, "Selection should be empty\n");
6085 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6086
6087 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6088 /* one two | three */
6089 send_ctrl_key(hwnd, VK_RIGHT);
6090 /* one two |three */
6091 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6092 ok(sel_start == sel_end, "Selection should be empty\n");
6093 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6094
6095 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6096 /* one two th|ree */
6097 send_ctrl_key(hwnd, VK_LEFT);
6098 /* one two |three */
6099 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6100 ok(sel_start == sel_end, "Selection should be empty\n");
6101 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6102
6103 /* Test with a custom word break procedure that uses X as the delimiter. */
6104 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6105 ok (result == TRUE, "Failed to clear the text.\n");
6106 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6107 /* |one twoXthree */
6108 send_ctrl_key(hwnd, VK_RIGHT);
6109 /* one twoX|three */
6110 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6111 ok(sel_start == sel_end, "Selection should be empty\n");
6112 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6113
6114 DestroyWindow(hwnd);
6115
6116 /* Make sure the behaviour is the same with a unicode richedit window,
6117 * and using unicode functions. */
6118
6119 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6120 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6121 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6122
6123 /* Test with a custom word break procedure that uses X as the delimiter. */
6124 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6125 ok (result == TRUE, "Failed to clear the text.\n");
6126 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6127 /* |one twoXthree */
6128 send_ctrl_key(hwnd, VK_RIGHT);
6129 /* one twoX|three */
6130 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6131 ok(sel_start == sel_end, "Selection should be empty\n");
6132 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6133
6134 DestroyWindow(hwnd);
6135 }
6136
6137 static void test_EM_CHARFROMPOS(void)
6138 {
6139 HWND hwnd;
6140 int result;
6141 RECT rcClient;
6142 POINTL point;
6143 point.x = 0;
6144 point.y = 40;
6145
6146 /* multi-line control inserts CR normally */
6147 hwnd = new_richedit(NULL);
6148 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6149 (LPARAM)"one two three four five six seven\reight");
6150 ok(result == 1, "Expected 1, got %d\n", result);
6151 GetClientRect(hwnd, &rcClient);
6152
6153 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6154 ok(result == 34, "expected character index of 34 but got %d\n", result);
6155
6156 /* Test with points outside the bounds of the richedit control. */
6157 point.x = -1;
6158 point.y = 40;
6159 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6160 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6161
6162 point.x = 1000;
6163 point.y = 0;
6164 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6165 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6166
6167 point.x = 1000;
6168 point.y = 36;
6169 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6170 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6171
6172 point.x = 1000;
6173 point.y = -1;
6174 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6175 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6176
6177 point.x = 1000;
6178 point.y = rcClient.bottom + 1;
6179 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6180 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6181
6182 point.x = 1000;
6183 point.y = rcClient.bottom;
6184 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6185 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6186
6187 DestroyWindow(hwnd);
6188 }
6189
6190 static void test_word_wrap(void)
6191 {
6192 HWND hwnd;
6193 POINTL point = {0, 60}; /* This point must be below the first line */
6194 const char *text = "Must be long enough to test line wrapping";
6195 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6196 int res, pos, lines;
6197
6198 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6199 * when specified on window creation and set later. */
6200 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6201 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6202 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6203 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6204 ok(res, "WM_SETTEXT failed.\n");
6205 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6206 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6207 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6208 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6209
6210 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6211 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6212 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6213 DestroyWindow(hwnd);
6214
6215 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6216 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6217 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6218
6219 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6220 ok(res, "WM_SETTEXT failed.\n");
6221 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6222 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6223 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6224 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6225
6226 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6227 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6228 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6229 DestroyWindow(hwnd);
6230
6231 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6232 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6233 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6234 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6235 ok(res, "WM_SETTEXT failed.\n");
6236 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6237 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6238
6239 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6240 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6241 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6242 DestroyWindow(hwnd);
6243
6244 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6245 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6246 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6247 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6248 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6249 ok(res, "WM_SETTEXT failed.\n");
6250 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6251 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6252
6253 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6254 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6255 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6256
6257 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6258 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6259 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6260 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6261 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6262
6263 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6264 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6265 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6266 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6267 DestroyWindow(hwnd);
6268
6269 /* Test to see if wrapping happens with redraw disabled. */
6270 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6271 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6272 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6273 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6274 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6275 ok(res, "EM_REPLACESEL failed.\n");
6276 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6277 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6278 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6279 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6280 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6281
6282 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6283 DestroyWindow(hwnd);
6284 }
6285
6286 static void test_autoscroll(void)
6287 {
6288 HWND hwnd = new_richedit(NULL);
6289 int lines, ret, redraw;
6290 POINT pt;
6291
6292 for (redraw = 0; redraw <= 1; redraw++) {
6293 trace("testing with WM_SETREDRAW=%d\n", redraw);
6294 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6295 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6296 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6297 ok(lines == 8, "%d lines instead of 8\n", lines);
6298 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6299 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6300 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6301 ret = GetWindowLongA(hwnd, GWL_STYLE);
6302 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6303
6304 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6305 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6306 ok(lines == 1, "%d lines instead of 1\n", lines);
6307 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6308 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6309 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6310 ret = GetWindowLongA(hwnd, GWL_STYLE);
6311 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6312 }
6313
6314 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6315 DestroyWindow(hwnd);
6316
6317 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6318 * auto vertical/horizontal scrolling options. */
6319 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6320 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6321 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6322 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6323 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6324 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6325 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6326 ret = GetWindowLongA(hwnd, GWL_STYLE);
6327 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6328 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6329 DestroyWindow(hwnd);
6330
6331 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6332 WS_POPUP|ES_MULTILINE,
6333 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6334 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6335 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6336 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6337 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6338 ret = GetWindowLongA(hwnd, GWL_STYLE);
6339 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6340 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6341 DestroyWindow(hwnd);
6342 }
6343
6344
6345 static void test_format_rect(void)
6346 {
6347 HWND hwnd;
6348 RECT rc, expected, clientRect;
6349 int n;
6350 DWORD options;
6351
6352 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6353 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6354 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6355 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6356
6357 GetClientRect(hwnd, &clientRect);
6358
6359 expected = clientRect;
6360 expected.left += 1;
6361 expected.right -= 1;
6362 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6363 ok(rc.top == expected.top && rc.left == expected.left &&
6364 rc.bottom == expected.bottom && rc.right == expected.right,
6365 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6366 rc.top, rc.left, rc.bottom, rc.right,
6367 expected.top, expected.left, expected.bottom, expected.right);
6368
6369 for (n = -3; n <= 3; n++)
6370 {
6371 rc = clientRect;
6372 rc.top += n;
6373 rc.left += n;
6374 rc.bottom -= n;
6375 rc.right -= n;
6376 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6377
6378 expected = rc;
6379 expected.top = max(0, rc.top);
6380 expected.left = max(0, rc.left);
6381 expected.bottom = min(clientRect.bottom, rc.bottom);
6382 expected.right = min(clientRect.right, rc.right);
6383 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6384 ok(rc.top == expected.top && rc.left == expected.left &&
6385 rc.bottom == expected.bottom && rc.right == expected.right,
6386 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6387 n, rc.top, rc.left, rc.bottom, rc.right,
6388 expected.top, expected.left, expected.bottom, expected.right);
6389 }
6390
6391 rc = clientRect;
6392 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6393 expected = clientRect;
6394 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6395 ok(rc.top == expected.top && rc.left == expected.left &&
6396 rc.bottom == expected.bottom && rc.right == expected.right,
6397 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6398 rc.top, rc.left, rc.bottom, rc.right,
6399 expected.top, expected.left, expected.bottom, expected.right);
6400
6401 /* Adding the selectionbar adds the selectionbar width to the left side. */
6402 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6403 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6404 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6405 expected.left += 8; /* selection bar width */
6406 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6407 ok(rc.top == expected.top && rc.left == expected.left &&
6408 rc.bottom == expected.bottom && rc.right == expected.right,
6409 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6410 rc.top, rc.left, rc.bottom, rc.right,
6411 expected.top, expected.left, expected.bottom, expected.right);
6412
6413 rc = clientRect;
6414 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6415 expected = clientRect;
6416 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6417 ok(rc.top == expected.top && rc.left == expected.left &&
6418 rc.bottom == expected.bottom && rc.right == expected.right,
6419 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6420 rc.top, rc.left, rc.bottom, rc.right,
6421 expected.top, expected.left, expected.bottom, expected.right);
6422
6423 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6424 * even if the left side is already 0. */
6425 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6426 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6427 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6428 expected.left -= 8; /* selection bar width */
6429 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6430 ok(rc.top == expected.top && rc.left == expected.left &&
6431 rc.bottom == expected.bottom && rc.right == expected.right,
6432 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6433 rc.top, rc.left, rc.bottom, rc.right,
6434 expected.top, expected.left, expected.bottom, expected.right);
6435
6436 /* Set the absolute value of the formatting rectangle. */
6437 rc = clientRect;
6438 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6439 expected = clientRect;
6440 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6441 ok(rc.top == expected.top && rc.left == expected.left &&
6442 rc.bottom == expected.bottom && rc.right == expected.right,
6443 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6444 n, rc.top, rc.left, rc.bottom, rc.right,
6445 expected.top, expected.left, expected.bottom, expected.right);
6446
6447 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6448 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6449 * tests show that this isn't true. */
6450 rc.top = 15;
6451 rc.left = 15;
6452 rc.bottom = clientRect.bottom - 15;
6453 rc.right = clientRect.right - 15;
6454 expected = rc;
6455 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6456 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6457 ok(rc.top == expected.top && rc.left == expected.left &&
6458 rc.bottom == expected.bottom && rc.right == expected.right,
6459 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6460 rc.top, rc.left, rc.bottom, rc.right,
6461 expected.top, expected.left, expected.bottom, expected.right);
6462
6463 /* For some reason it does not limit the values to the client rect with
6464 * a WPARAM value of 1. */
6465 rc.top = -15;
6466 rc.left = -15;
6467 rc.bottom = clientRect.bottom + 15;
6468 rc.right = clientRect.right + 15;
6469 expected = rc;
6470 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6471 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6472 ok(rc.top == expected.top && rc.left == expected.left &&
6473 rc.bottom == expected.bottom && rc.right == expected.right,
6474 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6475 rc.top, rc.left, rc.bottom, rc.right,
6476 expected.top, expected.left, expected.bottom, expected.right);
6477
6478 /* Reset to default rect and check how the format rect adjusts to window
6479 * resize and how it copes with very small windows */
6480 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6481
6482 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6483 GetClientRect(hwnd, &clientRect);
6484
6485 expected = clientRect;
6486 expected.left += 1;
6487 expected.right -= 1;
6488 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6489 ok(rc.top == expected.top && rc.left == expected.left &&
6490 rc.bottom == expected.bottom && rc.right == expected.right,
6491 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6492 rc.top, rc.left, rc.bottom, rc.right,
6493 expected.top, expected.left, expected.bottom, expected.right);
6494
6495 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6496 GetClientRect(hwnd, &clientRect);
6497
6498 expected = clientRect;
6499 expected.left += 1;
6500 expected.right -= 1;
6501 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6502 ok(rc.top == expected.top && rc.left == expected.left &&
6503 rc.bottom == expected.bottom && rc.right == expected.right,
6504 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6505 rc.top, rc.left, rc.bottom, rc.right,
6506 expected.top, expected.left, expected.bottom, expected.right);
6507
6508 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6509 GetClientRect(hwnd, &clientRect);
6510
6511 expected = clientRect;
6512 expected.left += 1;
6513 expected.right -= 1;
6514 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6515 ok(rc.top == expected.top && rc.left == expected.left &&
6516 rc.bottom == expected.bottom && rc.right == expected.right,
6517 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6518 rc.top, rc.left, rc.bottom, rc.right,
6519 expected.top, expected.left, expected.bottom, expected.right);
6520
6521 DestroyWindow(hwnd);
6522
6523 /* The extended window style affects the formatting rectangle. */
6524 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
6525 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6526 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6527 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6528
6529 GetClientRect(hwnd, &clientRect);
6530
6531 expected = clientRect;
6532 expected.left += 1;
6533 expected.top += 1;
6534 expected.right -= 1;
6535 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6536 ok(rc.top == expected.top && rc.left == expected.left &&
6537 rc.bottom == expected.bottom && rc.right == expected.right,
6538 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6539 rc.top, rc.left, rc.bottom, rc.right,
6540 expected.top, expected.left, expected.bottom, expected.right);
6541
6542 rc = clientRect;
6543 rc.top += 5;
6544 rc.left += 5;
6545 rc.bottom -= 5;
6546 rc.right -= 5;
6547 expected = rc;
6548 expected.top -= 1;
6549 expected.left -= 1;
6550 expected.right += 1;
6551 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6552 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6553 ok(rc.top == expected.top && rc.left == expected.left &&
6554 rc.bottom == expected.bottom && rc.right == expected.right,
6555 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6556 rc.top, rc.left, rc.bottom, rc.right,
6557 expected.top, expected.left, expected.bottom, expected.right);
6558
6559 DestroyWindow(hwnd);
6560 }
6561
6562 static void test_WM_GETDLGCODE(void)
6563 {
6564 HWND hwnd;
6565 UINT res, expected;
6566 MSG msg;
6567
6568 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6569
6570 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6571 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6572 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6573 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6574 msg.hwnd = hwnd;
6575 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6576 expected = expected | DLGC_WANTMESSAGE;
6577 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6578 res, expected);
6579 DestroyWindow(hwnd);
6580
6581 msg.message = WM_KEYDOWN;
6582 msg.wParam = VK_RETURN;
6583 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6584 msg.pt.x = 0;
6585 msg.pt.y = 0;
6586 msg.time = GetTickCount();
6587
6588 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6589 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6590 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6591 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6592 msg.hwnd = hwnd;
6593 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6594 expected = expected | DLGC_WANTMESSAGE;
6595 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6596 res, expected);
6597 DestroyWindow(hwnd);
6598
6599 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6600 ES_MULTILINE|WS_POPUP,
6601 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6602 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6603 msg.hwnd = hwnd;
6604 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6605 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6606 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6607 res, expected);
6608 DestroyWindow(hwnd);
6609
6610 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6611 ES_WANTRETURN|WS_POPUP,
6612 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6613 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6614 msg.hwnd = hwnd;
6615 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6616 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6617 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6618 res, expected);
6619 DestroyWindow(hwnd);
6620
6621 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6622 WS_POPUP,
6623 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6624 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6625 msg.hwnd = hwnd;
6626 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6627 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6628 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6629 res, expected);
6630 DestroyWindow(hwnd);
6631
6632 msg.wParam = VK_TAB;
6633 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6634
6635 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6636 ES_MULTILINE|WS_POPUP,
6637 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6638 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6639 msg.hwnd = hwnd;
6640 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6641 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6642 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6643 res, expected);
6644 DestroyWindow(hwnd);
6645
6646 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6647 WS_POPUP,
6648 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6649 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6650 msg.hwnd = hwnd;
6651 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6652 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6653 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6654 res, expected);
6655 DestroyWindow(hwnd);
6656
6657 hold_key(VK_CONTROL);
6658
6659 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6660 ES_MULTILINE|WS_POPUP,
6661 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6662 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6663 msg.hwnd = hwnd;
6664 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6665 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6666 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6667 res, expected);
6668 DestroyWindow(hwnd);
6669
6670 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6671 WS_POPUP,
6672 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6673 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6674 msg.hwnd = hwnd;
6675 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6676 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6677 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6678 res, expected);
6679 DestroyWindow(hwnd);
6680
6681 release_key(VK_CONTROL);
6682
6683 msg.wParam = 'a';
6684 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6685
6686 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6687 ES_MULTILINE|WS_POPUP,
6688 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6689 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6690 msg.hwnd = hwnd;
6691 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6692 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6693 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6694 res, expected);
6695 DestroyWindow(hwnd);
6696
6697 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6698 WS_POPUP,
6699 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6700 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6701 msg.hwnd = hwnd;
6702 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6703 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6704 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6705 res, expected);
6706 DestroyWindow(hwnd);
6707
6708 msg.message = WM_CHAR;
6709
6710 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6711 ES_MULTILINE|WS_POPUP,
6712 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6713 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6714 msg.hwnd = hwnd;
6715 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6716 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6717 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6718 res, expected);
6719 DestroyWindow(hwnd);
6720
6721 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6722 WS_POPUP,
6723 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6724 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6725 msg.hwnd = hwnd;
6726 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6727 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6728 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6729 res, expected);
6730 DestroyWindow(hwnd);
6731
6732 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6733 WS_POPUP|ES_SAVESEL,
6734 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6735 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6736 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
6737 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
6738 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6739 res, expected);
6740 DestroyWindow(hwnd);
6741 }
6742
6743 static void test_zoom(void)
6744 {
6745 HWND hwnd;
6746 UINT ret;
6747 RECT rc;
6748 POINT pt;
6749 int numerator, denominator;
6750
6751 hwnd = new_richedit(NULL);
6752 GetClientRect(hwnd, &rc);
6753 pt.x = (rc.right - rc.left) / 2;
6754 pt.y = (rc.bottom - rc.top) / 2;
6755 ClientToScreen(hwnd, &pt);
6756
6757 /* Test initial zoom value */
6758 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6759 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6760 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6761 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6762
6763 /* test scroll wheel */
6764 hold_key(VK_CONTROL);
6765 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6766 MAKELPARAM(pt.x, pt.y));
6767 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6768 release_key(VK_CONTROL);
6769
6770 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6771 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6772 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6773 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6774
6775 /* Test how much the mouse wheel can zoom in and out. */
6776 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
6777 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6778
6779 hold_key(VK_CONTROL);
6780 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6781 MAKELPARAM(pt.x, pt.y));
6782 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6783 release_key(VK_CONTROL);
6784
6785 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6786 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6787 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6788 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6789
6790 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
6791 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6792
6793 hold_key(VK_CONTROL);
6794 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6795 MAKELPARAM(pt.x, pt.y));
6796 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6797 release_key(VK_CONTROL);
6798
6799 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6800 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6801 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6802 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6803
6804 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
6805 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6806
6807 hold_key(VK_CONTROL);
6808 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6809 MAKELPARAM(pt.x, pt.y));
6810 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6811 release_key(VK_CONTROL);
6812
6813 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6814 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6815 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6816 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6817
6818 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
6819 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6820
6821 hold_key(VK_CONTROL);
6822 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6823 MAKELPARAM(pt.x, pt.y));
6824 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6825 release_key(VK_CONTROL);
6826
6827 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6828 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6829 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6830 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6831
6832 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6833 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
6834 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6835
6836 hold_key(VK_CONTROL);
6837 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6838 MAKELPARAM(pt.x, pt.y));
6839 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6840 release_key(VK_CONTROL);
6841
6842 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6843 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6844 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6845 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6846
6847 /* Test bounds checking on EM_SETZOOM */
6848 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
6849 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6850
6851 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
6852 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6853
6854 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
6855 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6856
6857 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6858 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6859 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6860 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6861
6862 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
6863 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6864
6865 /* See if negative numbers are accepted. */
6866 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
6867 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6868
6869 /* See if negative numbers are accepted. */
6870 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
6871 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6872
6873 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6874 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6875 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6876 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6877
6878 /* Reset the zoom value */
6879 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
6880 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6881
6882 DestroyWindow(hwnd);
6883 }
6884
6885 struct dialog_mode_messages
6886 {
6887 int wm_getdefid, wm_close, wm_nextdlgctl;
6888 };
6889
6890 static struct dialog_mode_messages dm_messages;
6891
6892 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6893 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6894 "got %d\n", wmclose, dm_messages.wm_close); \
6895 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6896 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6897 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6898 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6899
6900 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6901 {
6902 switch (iMsg)
6903 {
6904 case DM_GETDEFID:
6905 dm_messages.wm_getdefid++;
6906 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6907 case WM_NEXTDLGCTL:
6908 dm_messages.wm_nextdlgctl++;
6909 break;
6910 case WM_CLOSE:
6911 dm_messages.wm_close++;
6912 break;
6913 }
6914
6915 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
6916 }
6917
6918 static void test_dialogmode(void)
6919 {
6920 HWND hwRichEdit, hwParent, hwButton;
6921 MSG msg= {0};
6922 int lcount, r;
6923 WNDCLASSA cls;
6924
6925 cls.style = 0;
6926 cls.lpfnWndProc = dialog_mode_wnd_proc;
6927 cls.cbClsExtra = 0;
6928 cls.cbWndExtra = 0;
6929 cls.hInstance = GetModuleHandleA(0);
6930 cls.hIcon = 0;
6931 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6932 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6933 cls.lpszMenuName = NULL;
6934 cls.lpszClassName = "DialogModeParentClass";
6935 if(!RegisterClassA(&cls)) assert(0);
6936
6937 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6938 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6939
6940 /* Test richedit(ES_MULTILINE) */
6941
6942 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
6943
6944 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6945 ok(0 == r, "expected 0, got %d\n", r);
6946 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6947 ok(2 == lcount, "expected 2, got %d\n", lcount);
6948
6949 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
6950 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6951
6952 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6953 ok(0 == r, "expected 0, got %d\n", r);
6954 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6955 ok(3 == lcount, "expected 3, got %d\n", lcount);
6956
6957 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6958 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6959 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6960 ok(0 == r, "expected 0, got %d\n", r);
6961 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6962 ok(3 == lcount, "expected 3, got %d\n", lcount);
6963
6964 DestroyWindow(hwRichEdit);
6965
6966 /* Test standalone richedit(ES_MULTILINE) */
6967
6968 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
6969
6970 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6971 ok(0 == r, "expected 0, got %d\n", r);
6972 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6973 ok(2 == lcount, "expected 2, got %d\n", lcount);
6974
6975 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6976 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6977
6978 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6979 ok(0 == r, "expected 0, got %d\n", r);
6980 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6981 ok(2 == lcount, "expected 2, got %d\n", lcount);
6982
6983 DestroyWindow(hwRichEdit);
6984
6985 /* Check a destination for messages */
6986
6987 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
6988
6989 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6990 SetParent( hwRichEdit, NULL);
6991
6992 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6993 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6994
6995 memset(&dm_messages, 0, sizeof(dm_messages));
6996 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6997 ok(0 == r, "expected 0, got %d\n", r);
6998 test_dm_messages(0, 1, 0);
6999
7000 memset(&dm_messages, 0, sizeof(dm_messages));
7001 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7002 ok(0 == r, "expected 0, got %d\n", r);
7003 test_dm_messages(0, 0, 1);
7004
7005 DestroyWindow(hwRichEdit);
7006
7007 /* Check messages from richedit(ES_MULTILINE) */
7008
7009 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7010
7011 memset(&dm_messages, 0, sizeof(dm_messages));
7012 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7013 ok(0 == r, "expected 0, got %d\n", r);
7014 test_dm_messages(0, 0, 0);
7015
7016 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7017 ok(2 == lcount, "expected 2, got %d\n", lcount);
7018
7019 memset(&dm_messages, 0, sizeof(dm_messages));
7020 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7021 ok(0 == r, "expected 0, got %d\n", r);
7022 test_dm_messages(0, 0, 0);
7023
7024 memset(&dm_messages, 0, sizeof(dm_messages));
7025 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7026 ok(0 == r, "expected 0, got %d\n", r);
7027 test_dm_messages(0, 0, 0);
7028
7029 memset(&dm_messages, 0, sizeof(dm_messages));
7030 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7031 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7032 test_dm_messages(0, 0, 0);
7033
7034 memset(&dm_messages, 0, sizeof(dm_messages));
7035 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7036 ok(0 == r, "expected 0, got %d\n", r);
7037 test_dm_messages(0, 1, 0);
7038
7039 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7040 ok(2 == lcount, "expected 2, got %d\n", lcount);
7041
7042 memset(&dm_messages, 0, sizeof(dm_messages));
7043 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7044 ok(0 == r, "expected 0, got %d\n", r);
7045 test_dm_messages(0, 0, 0);
7046
7047 memset(&dm_messages, 0, sizeof(dm_messages));
7048 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7049 ok(0 == r, "expected 0, got %d\n", r);
7050 test_dm_messages(0, 0, 1);
7051
7052 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7053 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7054 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7055
7056 memset(&dm_messages, 0, sizeof(dm_messages));
7057 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7058 ok(0 == r, "expected 0, got %d\n", r);
7059 test_dm_messages(0, 1, 1);
7060
7061 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7062 ok(2 == lcount, "expected 2, got %d\n", lcount);
7063
7064 DestroyWindow(hwButton);
7065 DestroyWindow(hwRichEdit);
7066
7067 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7068
7069 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7070
7071 memset(&dm_messages, 0, sizeof(dm_messages));
7072 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7073 ok(0 == r, "expected 0, got %d\n", r);
7074 test_dm_messages(0, 0, 0);
7075
7076 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7077 ok(2 == lcount, "expected 2, got %d\n", lcount);
7078
7079 memset(&dm_messages, 0, sizeof(dm_messages));
7080 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7081 ok(0 == r, "expected 0, got %d\n", r);
7082 test_dm_messages(0, 0, 0);
7083
7084 memset(&dm_messages, 0, sizeof(dm_messages));
7085 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7086 ok(0 == r, "expected 0, got %d\n", r);
7087 test_dm_messages(0, 0, 0);
7088
7089 memset(&dm_messages, 0, sizeof(dm_messages));
7090 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7091 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7092 test_dm_messages(0, 0, 0);
7093
7094 memset(&dm_messages, 0, sizeof(dm_messages));
7095 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7096 ok(0 == r, "expected 0, got %d\n", r);
7097 test_dm_messages(0, 0, 0);
7098
7099 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7100 ok(3 == lcount, "expected 3, got %d\n", lcount);
7101
7102 memset(&dm_messages, 0, sizeof(dm_messages));
7103 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7104 ok(0 == r, "expected 0, got %d\n", r);
7105 test_dm_messages(0, 0, 0);
7106
7107 memset(&dm_messages, 0, sizeof(dm_messages));
7108 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7109 ok(0 == r, "expected 0, got %d\n", r);
7110 test_dm_messages(0, 0, 1);
7111
7112 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7113 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7114 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7115
7116 memset(&dm_messages, 0, sizeof(dm_messages));
7117 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7118 ok(0 == r, "expected 0, got %d\n", r);
7119 test_dm_messages(0, 0, 0);
7120
7121 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7122 ok(4 == lcount, "expected 4, got %d\n", lcount);
7123
7124 DestroyWindow(hwButton);
7125 DestroyWindow(hwRichEdit);
7126
7127 /* Check messages from richedit(0) */
7128
7129 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7130
7131 memset(&dm_messages, 0, sizeof(dm_messages));
7132 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7133 ok(0 == r, "expected 0, got %d\n", r);
7134 test_dm_messages(0, 0, 0);
7135
7136 memset(&dm_messages, 0, sizeof(dm_messages));
7137 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7138 ok(0 == r, "expected 0, got %d\n", r);
7139 test_dm_messages(0, 0, 0);
7140
7141 memset(&dm_messages, 0, sizeof(dm_messages));
7142 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7143 ok(0 == r, "expected 0, got %d\n", r);
7144 test_dm_messages(0, 0, 0);
7145
7146 memset(&dm_messages, 0, sizeof(dm_messages));
7147 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7148 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7149 test_dm_messages(0, 0, 0);
7150
7151 memset(&dm_messages, 0, sizeof(dm_messages));
7152 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7153 ok(0 == r, "expected 0, got %d\n", r);
7154 test_dm_messages(0, 1, 0);
7155
7156 memset(&dm_messages, 0, sizeof(dm_messages));
7157 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7158 ok(0 == r, "expected 0, got %d\n", r);
7159 test_dm_messages(0, 0, 0);
7160
7161 memset(&dm_messages, 0, sizeof(dm_messages));
7162 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7163 ok(0 == r, "expected 0, got %d\n", r);
7164 test_dm_messages(0, 0, 1);
7165
7166 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7167 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7168 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7169
7170 memset(&dm_messages, 0, sizeof(dm_messages));
7171 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7172 ok(0 == r, "expected 0, got %d\n", r);
7173 test_dm_messages(0, 1, 1);
7174
7175 DestroyWindow(hwRichEdit);
7176
7177 /* Check messages from richedit(ES_WANTRETURN) */
7178
7179 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7180
7181 memset(&dm_messages, 0, sizeof(dm_messages));
7182 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7183 ok(0 == r, "expected 0, got %d\n", r);
7184 test_dm_messages(0, 0, 0);
7185
7186 memset(&dm_messages, 0, sizeof(dm_messages));
7187 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7188 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7189 test_dm_messages(0, 0, 0);
7190
7191 memset(&dm_messages, 0, sizeof(dm_messages));
7192 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7193 ok(0 == r, "expected 0, got %d\n", r);
7194 test_dm_messages(0, 0, 0);
7195
7196 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7197 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7198 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7199
7200 memset(&dm_messages, 0, sizeof(dm_messages));
7201 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7202 ok(0 == r, "expected 0, got %d\n", r);
7203 test_dm_messages(0, 0, 0);
7204
7205 DestroyWindow(hwRichEdit);
7206 DestroyWindow(hwParent);
7207 }
7208
7209 static void test_EM_FINDWORDBREAK_W(void)
7210 {
7211 static const struct {
7212 WCHAR c;
7213 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7214 } delimiter_tests[] = {
7215 {0x0a, FALSE}, /* newline */
7216 {0x0b, FALSE}, /* vertical tab */
7217 {0x0c, FALSE}, /* form feed */
7218 {0x0d, FALSE}, /* carriage return */
7219 {0x20, TRUE}, /* space */
7220 {0x61, FALSE}, /* capital letter a */
7221 {0xa0, FALSE}, /* no-break space */
7222 {0x2000, FALSE}, /* en quad */
7223 {0x3000, FALSE}, /* Ideographic space */
7224 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7225 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7226 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7227 {0xac00, FALSE}, /* Hangul character GA*/
7228 {0xd7af, FALSE}, /* End of Hangul character chart */
7229 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7230 {0xff20, FALSE}, /* fullwidth commercial @ */
7231 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7232 };
7233 int i;
7234 HWND hwndRichEdit = new_richeditW(NULL);
7235 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7236 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7237 {
7238 WCHAR wbuf[2];
7239 int result;
7240
7241 wbuf[0] = delimiter_tests[i].c;
7242 wbuf[1] = 0;
7243 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7244 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7245 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7246 todo_wine
7247 ok(result == delimiter_tests[i].isdelimiter,
7248 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7249 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7250 else
7251 ok(result == delimiter_tests[i].isdelimiter,
7252 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7253 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7254 }
7255 DestroyWindow(hwndRichEdit);
7256 }
7257
7258 static void test_EM_FINDWORDBREAK_A(void)
7259 {
7260 static const struct {
7261 WCHAR c;
7262 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7263 } delimiter_tests[] = {
7264 {0x0a, FALSE}, /* newline */
7265 {0x0b, FALSE}, /* vertical tab */
7266 {0x0c, FALSE}, /* form feed */
7267 {0x0d, FALSE}, /* carriage return */
7268 {0x20, TRUE}, /* space */
7269 {0x61, FALSE}, /* capital letter a */
7270 };
7271 int i;
7272 HWND hwndRichEdit = new_richedit(NULL);
7273
7274 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7275 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7276 {
7277 int result;
7278 char buf[2];
7279 buf[0] = delimiter_tests[i].c;
7280 buf[1] = 0;
7281 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7282 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7283 if (buf[0] == 0x20)
7284 todo_wine
7285 ok(result == delimiter_tests[i].isdelimiter,
7286 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7287 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7288 else
7289 ok(result == delimiter_tests[i].isdelimiter,
7290 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7291 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7292 }
7293 DestroyWindow(hwndRichEdit);
7294 }
7295
7296 /*
7297 * This test attempts to show the effect of enter on a richedit
7298 * control v1.0 inserts CRLF whereas for higher versions it only
7299 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7300 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7301 * does for higher. The same test is cloned in riched32 and riched20.
7302 */
7303 static void test_enter(void)
7304 {
7305 static const struct {
7306 const char *initialtext;
7307 const int cursor;
7308 const char *expectedwmtext;
7309 const char *expectedemtext;
7310 const char *expectedemtextcrlf;
7311 } testenteritems[] = {
7312 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7313 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7314 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7315 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7316 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7317 };
7318
7319 char expectedbuf[1024];
7320 char resultbuf[1024];
7321 HWND hwndRichEdit = new_richedit(NULL);
7322 UINT i,j;
7323
7324 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7325
7326 char buf[1024] = {0};
7327 LRESULT result;
7328 GETTEXTEX getText;
7329 const char *expected;
7330
7331 /* Set the text to the initial text */
7332 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7333 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7334
7335 /* Send Enter */
7336 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7337 simulate_typing_characters(hwndRichEdit, "\r");
7338
7339 /* 1. Retrieve with WM_GETTEXT */
7340 buf[0] = 0x00;
7341 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7342 expected = testenteritems[i].expectedwmtext;
7343
7344 resultbuf[0]=0x00;
7345 for (j = 0; j < (UINT)result; j++)
7346 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7347 expectedbuf[0] = '\0';
7348 for (j = 0; j < strlen(expected); j++)
7349 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7350
7351 result = strcmp(expected, buf);
7352 ok (result == 0,
7353 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7354 i, resultbuf, expectedbuf);
7355
7356 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7357 getText.cb = sizeof(buf);
7358 getText.flags = GT_DEFAULT;
7359 getText.codepage = CP_ACP;
7360 getText.lpDefaultChar = NULL;
7361 getText.lpUsedDefChar = NULL;
7362 buf[0] = 0x00;
7363 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7364 expected = testenteritems[i].expectedemtext;
7365
7366 resultbuf[0]=0x00;
7367 for (j = 0; j < (UINT)result; j++)
7368 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7369 expectedbuf[0] = '\0';
7370 for (j = 0; j < strlen(expected); j++)
7371 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7372
7373 result = strcmp(expected, buf);
7374 ok (result == 0,
7375 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7376 i, resultbuf, expectedbuf);
7377
7378 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7379 getText.cb = sizeof(buf);
7380 getText.flags = GT_USECRLF;
7381 getText.codepage = CP_ACP;
7382 getText.lpDefaultChar = NULL;
7383 getText.lpUsedDefChar = NULL;
7384 buf[0] = 0x00;
7385 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7386 expected = testenteritems[i].expectedemtextcrlf;
7387
7388 resultbuf[0]=0x00;
7389 for (j = 0; j < (UINT)result; j++)
7390 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7391 expectedbuf[0] = '\0';
7392 for (j = 0; j < strlen(expected); j++)
7393 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7394
7395 result = strcmp(expected, buf);
7396 ok (result == 0,
7397 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7398 i, resultbuf, expectedbuf);
7399 }
7400
7401 DestroyWindow(hwndRichEdit);
7402 }
7403
7404 static void test_WM_CREATE(void)
7405 {
7406 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7407 static const char title[] = "line1\nline2";
7408
7409 HWND rich_edit;
7410 LRESULT res;
7411 char buf[64];
7412 int len;
7413
7414 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7415 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7416 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7417
7418 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7419 ok(len == 5, "GetWindowText returned %d\n", len);
7420 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7421
7422 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7423 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7424
7425 DestroyWindow(rich_edit);
7426
7427 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7428 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7429 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7430
7431 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7432 ok(len == 12, "GetWindowText returned %d\n", len);
7433 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7434
7435 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7436 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7437
7438 DestroyWindow(rich_edit);
7439 }
7440
7441 /*******************************************************************
7442 * Test that after deleting all of the text, the first paragraph
7443 * format reverts to the default.
7444 */
7445 static void test_reset_default_para_fmt( void )
7446 {
7447 HWND richedit = new_richeditW( NULL );
7448 PARAFORMAT2 fmt;
7449 WORD def_align, new_align;
7450
7451 memset( &fmt, 0, sizeof(fmt) );
7452 fmt.cbSize = sizeof(PARAFORMAT2);
7453 fmt.dwMask = -1;
7454 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7455 def_align = fmt.wAlignment;
7456 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7457
7458 simulate_typing_characters( richedit, "123" );
7459
7460 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7461 fmt.dwMask = PFM_ALIGNMENT;
7462 fmt.wAlignment = new_align;
7463 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7464
7465 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7466 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7467
7468 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7469 SendMessageA( richedit, WM_CUT, 0, 0 );
7470
7471 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7472 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7473
7474 DestroyWindow( richedit );
7475 }
7476
7477 START_TEST( editor )
7478 {
7479 BOOL ret;
7480 /* Must explicitly LoadLibrary(). The test has no references to functions in
7481 * RICHED20.DLL, so the linker doesn't actually link to it. */
7482 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7483 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7484
7485 test_WM_CHAR();
7486 test_EM_FINDTEXT(FALSE);
7487 test_EM_FINDTEXT(TRUE);
7488 test_EM_GETLINE();
7489 test_EM_POSFROMCHAR();
7490 test_EM_SCROLLCARET();
7491 test_EM_SCROLL();
7492 test_scrollbar_visibility();
7493 test_WM_SETTEXT();
7494 test_EM_LINELENGTH();
7495 test_EM_SETCHARFORMAT();
7496 test_EM_SETTEXTMODE();
7497 test_TM_PLAINTEXT();
7498 test_EM_SETOPTIONS();
7499 test_WM_GETTEXT();
7500 test_EM_GETTEXTRANGE();
7501 test_EM_GETSELTEXT();
7502 test_EM_SETUNDOLIMIT();
7503 test_ES_PASSWORD();
7504 test_EM_SETTEXTEX();
7505 test_EM_LIMITTEXT();
7506 test_EM_EXLIMITTEXT();
7507 test_EM_GETLIMITTEXT();
7508 test_WM_SETFONT();
7509 test_EM_GETMODIFY();
7510 test_EM_EXSETSEL();
7511 test_WM_PASTE();
7512 test_EM_STREAMIN();
7513 test_EM_STREAMOUT();
7514 test_EM_STREAMOUT_FONTTBL();
7515 test_EM_StreamIn_Undo();
7516 test_EM_FORMATRANGE();
7517 test_unicode_conversions();
7518 test_EM_GETTEXTLENGTHEX();
7519 test_EM_REPLACESEL(1);
7520 test_EM_REPLACESEL(0);
7521 test_WM_NOTIFY();
7522 test_EM_AUTOURLDETECT();
7523 test_eventMask();
7524 test_undo_coalescing();
7525 test_word_movement();
7526 test_EM_CHARFROMPOS();
7527 test_SETPARAFORMAT();
7528 test_word_wrap();
7529 test_autoscroll();
7530 test_format_rect();
7531 test_WM_GETDLGCODE();
7532 test_zoom();
7533 test_dialogmode();
7534 test_EM_FINDWORDBREAK_W();
7535 test_EM_FINDWORDBREAK_A();
7536 test_enter();
7537 test_WM_CREATE();
7538 test_reset_default_para_fmt();
7539
7540 /* Set the environment variable WINETEST_RICHED20 to keep windows
7541 * responsive and open for 30 seconds. This is useful for debugging.
7542 */
7543 if (getenv( "WINETEST_RICHED20" )) {
7544 keep_responsive(30);
7545 }
7546
7547 OleFlushClipboard();
7548 ret = FreeLibrary(hmoduleRichEdit);
7549 ok(ret, "error: %d\n", (int) GetLastError());
7550 }