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