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