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