ReactOS 0.4.15-dev-6703-g6528ab8
editor.c
Go to the documentation of this file.
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#define COBJMACROS
24
25#include <stdarg.h>
26#include <stdio.h>
27#include <assert.h>
28#include <windef.h>
29#include <winbase.h>
30#include <wingdi.h>
31#include <winuser.h>
32#include <winnls.h>
33#include <ole2.h>
34#include <richedit.h>
35#include <richole.h>
36#include <commdlg.h>
37#include <time.h>
38#include <wine/test.h>
39
40#define ID_RICHEDITTESTDBUTTON 0x123
41
43
44#define ok_w3(format, szString1, szString2, szString3) \
45 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
46 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
47 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
48 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
49 format, string1, string2, string3);
50
53
54static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
55 HWND hwnd;
56 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
57 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
59 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60 return hwnd;
61}
62
63static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
64 HWND hwnd;
65 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
66 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
68 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
69 return hwnd;
70}
71
74}
75
78}
79
82}
83
84/* Keeps the window reponsive for the deley_time in seconds.
85 * This is useful for debugging a test to see what is happening. */
86static void keep_responsive(time_t delay_time)
87{
88 MSG msg;
89 time_t end;
90
91 /* The message pump uses PeekMessage() to empty the queue and then
92 * sleeps for 50ms before retrying the queue. */
93 end = time(NULL) + delay_time;
94 while (time(NULL) < end) {
95 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
98 } else {
99 Sleep(50);
100 }
101 }
102}
103
104static void simulate_typing_characters(HWND hwnd, const char* szChars)
105{
106 int ret;
107
108 while (*szChars != '\0') {
109 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
110 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
111 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
112 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
113 szChars++;
114 }
115}
116
117static BOOL hold_key(int vk)
118{
119 BYTE key_state[256];
120 BOOL result;
121
122 result = GetKeyboardState(key_state);
123 ok(result, "GetKeyboardState failed.\n");
124 if (!result) return FALSE;
125 key_state[vk] |= 0x80;
126 result = SetKeyboardState(key_state);
127 ok(result, "SetKeyboardState failed.\n");
128 return result != 0;
129}
130
131static BOOL release_key(int vk)
132{
133 BYTE key_state[256];
134 BOOL result;
135
136 result = GetKeyboardState(key_state);
137 ok(result, "GetKeyboardState failed.\n");
138 if (!result) return FALSE;
139 key_state[vk] &= ~0x80;
140 result = SetKeyboardState(key_state);
141 ok(result, "SetKeyboardState failed.\n");
142 return result != 0;
143}
144
145static const char haystack[] = "WINEWine wineWine wine WineWine";
146 /* ^0 ^10 ^20 ^30 */
147
148struct find_s {
149 int start;
150 int end;
151 const char *needle;
152 int flags;
154};
155
156
157static struct find_s find_tests[] = {
158 /* Find in empty text */
159 {0, -1, "foo", FR_DOWN, -1},
160 {0, -1, "foo", 0, -1},
161 {0, -1, "", FR_DOWN, -1},
162 {20, 5, "foo", FR_DOWN, -1},
163 {5, 20, "foo", FR_DOWN, -1}
164};
165
166static struct find_s find_tests2[] = {
167 /* No-result find */
168 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
169 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170
171 /* Subsequent finds */
172 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
173 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
174 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
175 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176
177 /* Find backwards */
178 {19, 20, "Wine", FR_MATCHCASE, 13},
179 {10, 20, "Wine", FR_MATCHCASE, 4},
180 {20, 10, "Wine", FR_MATCHCASE, 13},
181
182 /* Case-insensitive */
183 {1, 31, "wInE", FR_DOWN, 4},
184 {1, 31, "Wine", FR_DOWN, 4},
185
186 /* High-to-low ranges */
187 {20, 5, "Wine", FR_DOWN, -1},
188 {2, 1, "Wine", FR_DOWN, -1},
189 {30, 29, "Wine", FR_DOWN, -1},
190 {20, 5, "Wine", 0, 13},
191
192 /* Find nothing */
193 {5, 10, "", FR_DOWN, -1},
194 {10, 5, "", FR_DOWN, -1},
195 {0, -1, "", FR_DOWN, -1},
196 {10, 5, "", 0, -1},
197
198 /* Whole-word search */
199 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
200 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
201 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
202 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
203 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
204 {11, -1, "winewine", FR_WHOLEWORD, 0},
205 {31, -1, "winewine", FR_WHOLEWORD, 23},
206
207 /* Bad ranges */
208 {5, 200, "XXX", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-20, 20, "Wine", FR_DOWN, -1},
211 {-15, -20, "Wine", FR_DOWN, -1},
212 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213
214 /* Check the case noted in bug 4479 where matches at end aren't recognized */
215 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
216 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220
221 /* The backwards case of bug 4479; bounds look right
222 * Fails because backward find is wrong */
223 {19, 20, "WINE", FR_MATCHCASE, 0},
224 {0, 20, "WINE", FR_MATCHCASE, -1},
225
226 {0, -1, "wineWine wine", 0, -1},
227};
228
229static WCHAR *atowstr(const char *str)
230{
231 WCHAR *ret;
232 DWORD len;
233 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
234 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
236 return ret;
237}
238
239static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
240{
241 int findloc;
242
243 if(unicode){
244 FINDTEXTW ftw;
245 memset(&ftw, 0, sizeof(ftw));
246 ftw.chrg.cpMin = f->start;
247 ftw.chrg.cpMax = f->end;
248 ftw.lpstrText = atowstr(f->needle);
249
250 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
251 ok(findloc == f->expected_loc,
252 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
253 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254
255 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
256 ok(findloc == f->expected_loc,
257 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
258 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
259
260 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
261 }else{
262 FINDTEXTA fta;
263 memset(&fta, 0, sizeof(fta));
264 fta.chrg.cpMin = f->start;
265 fta.chrg.cpMax = f->end;
266 fta.lpstrText = f->needle;
267
268 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
269 ok(findloc == f->expected_loc,
270 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
271 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
272 }
273}
274
275static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
276 int id, BOOL unicode)
277{
278 int findloc;
279 int expected_end_loc;
280
281 if(unicode){
282 FINDTEXTEXW ftw;
283 memset(&ftw, 0, sizeof(ftw));
284 ftw.chrg.cpMin = f->start;
285 ftw.chrg.cpMax = f->end;
286 ftw.lpstrText = atowstr(f->needle);
287 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
288 ok(findloc == f->expected_loc,
289 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
290 name, id, f->needle, f->start, f->end, f->flags, findloc);
291 ok(ftw.chrgText.cpMin == f->expected_loc,
292 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
293 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
294 expected_end_loc = ((f->expected_loc == -1) ? -1
295 : f->expected_loc + strlen(f->needle));
296 ok(ftw.chrgText.cpMax == expected_end_loc,
297 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
298 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
299 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
300 }else{
301 FINDTEXTEXA fta;
302 memset(&fta, 0, sizeof(fta));
303 fta.chrg.cpMin = f->start;
304 fta.chrg.cpMax = f->end;
305 fta.lpstrText = f->needle;
306 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
307 ok(findloc == f->expected_loc,
308 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
309 name, id, f->needle, f->start, f->end, f->flags, findloc);
310 ok(fta.chrgText.cpMin == f->expected_loc,
311 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
312 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
313 expected_end_loc = ((f->expected_loc == -1) ? -1
314 : f->expected_loc + strlen(f->needle));
315 ok(fta.chrgText.cpMax == expected_end_loc,
316 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
317 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
318 }
319}
320
321static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
322 int num_tests, BOOL unicode)
323{
324 int i;
325
326 for (i = 0; i < num_tests; i++) {
327 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
328 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
329 }
330}
331
332static void test_EM_FINDTEXT(BOOL unicode)
333{
334 HWND hwndRichEdit;
335 CHARFORMAT2A cf2;
336
337 if(unicode)
338 hwndRichEdit = new_richeditW(NULL);
339 else
340 hwndRichEdit = new_richedit(NULL);
341
342 /* Empty rich edit control */
343 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests), unicode);
344
345 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
346
347 /* Haystack text */
348 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2), unicode);
349
350 /* Setting a format on an arbitrary range should have no effect in search
351 results. This tests correct offset reporting across runs. */
352 cf2.cbSize = sizeof(CHARFORMAT2A);
353 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
354 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
355 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
356 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
357 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
358
359 /* Haystack text, again */
360 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
361
362 /* Yet another range */
363 cf2.dwMask = CFM_BOLD | cf2.dwMask;
364 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
365 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
366 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
367
368 /* Haystack text, again */
369 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
370
371 DestroyWindow(hwndRichEdit);
372}
373
374static const struct getline_s {
375 int line;
377 const char *text;
378} gl[] = {
379 {0, 10, "foo bar\r"},
380 {1, 10, "\r"},
381 {2, 10, "bar\r"},
382 {3, 10, "\r"},
383
384 /* Buffer smaller than line length */
385 {0, 2, "foo bar\r"},
386 {0, 1, "foo bar\r"},
387 {0, 0, "foo bar\r"}
389
390static void test_EM_GETLINE(void)
391{
392 int i;
393 HWND hwndRichEdit = new_richedit(NULL);
394 static const int nBuf = 1024;
395 char dest[1024], origdest[1024];
396 const char text[] = "foo bar\n"
397 "\n"
398 "bar\n";
399
400 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
401
402 memset(origdest, 0xBB, nBuf);
403 for (i = 0; i < ARRAY_SIZE(gl); i++)
404 {
405 int nCopied;
406 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
407 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
408 memset(dest, 0xBB, nBuf);
409 *(WORD *) dest = gl[i].buffer_len;
410
411 /* EM_GETLINE appends a "\r\0" to the end of the line
412 * nCopied counts up to and including the '\r' */
413 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
414 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
415 expected_nCopied);
416 /* two special cases since a parameter is passed via dest */
417 if (gl[i].buffer_len == 0)
418 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
419 "buffer_len=0\n");
420 else if (gl[i].buffer_len == 1)
421 ok(dest[0] == gl[i].text[0] && !dest[1] &&
422 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
423 else
424 {
425 /* Prepare hex strings of buffers to dump on failure. */
426 char expectedbuf[1024];
427 char resultbuf[1024];
428 int j;
429 resultbuf[0] = '\0';
430 for (j = 0; j < 32; j++)
431 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
432 expectedbuf[0] = '\0';
433 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
434 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
435 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
436 sprintf(expectedbuf+strlen(expectedbuf), "??");
437 for (; j < 32; j++) /* Bytes after declared buffer size */
438 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
439
440 /* Test the part of the buffer that is expected to be written according
441 * to the MSDN documentation fo EM_GETLINE, which does not state that
442 * a NULL terminating character will be added unless no text is copied.
443 *
444 * Windows NT does not append a NULL terminating character, but
445 * Windows 2000 and up do append a NULL terminating character if there
446 * is space in the buffer. The test will ignore this difference. */
447 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
448 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
449 i, expected_bytes_written, expectedbuf, resultbuf);
450 /* Test the part of the buffer after the declared length to make sure
451 * there are no buffer overruns. */
452 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
453 nBuf - gl[i].buffer_len),
454 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
455 i, expected_bytes_written, expectedbuf, resultbuf);
456 }
457 }
458
459 DestroyWindow(hwndRichEdit);
460}
461
462static void test_EM_LINELENGTH(void)
463{
464 HWND hwndRichEdit = new_richedit(NULL);
465 const char * text =
466 "richedit1\r"
467 "richedit1\n"
468 "richedit1\r\n"
469 "richedit1";
470 int offset_test[10][2] = {
471 {0, 9},
472 {5, 9},
473 {10, 9},
474 {15, 9},
475 {20, 9},
476 {25, 9},
477 {30, 9},
478 {35, 9},
479 {40, 0},
480 {45, 0},
481 };
482 int i;
484
485 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
486
487 for (i = 0; i < 10; i++) {
488 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
489 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
490 offset_test[i][0], result, offset_test[i][1]);
491 }
492
493 /* Test with multibyte character */
494 if (!is_lang_japanese)
495 skip("Skip multibyte character tests on non-Japanese platform\n");
496 else
497 {
498 const char *text1 =
499 "wine\n"
500 "richedit\x8e\xf0\n"
501 "wine";
502 int offset_test1[3][2] = {
503 {0, 4}, /* Line 1: |wine\n */
504 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
505 {15, 4}, /* Line 3: |wine */
506 };
507 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
508 for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
509 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
510 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
511 offset_test1[i][0], result, offset_test1[i][1]);
512 }
513 }
514
515 DestroyWindow(hwndRichEdit);
516}
517
519{
520 POINT p = {-1, -1};
522 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
523 return p.y;
524}
525
526static void move_cursor(HWND hwnd, LONG charindex)
527{
528 CHARRANGE cr;
529 cr.cpMax = charindex;
530 cr.cpMin = charindex;
532}
533
534static void line_scroll(HWND hwnd, int amount)
535{
536 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
537}
538
539static void test_EM_SCROLLCARET(void)
540{
541 int prevY, curY;
542 const char text[] = "aa\n"
543 "this is a long line of text that should be longer than the "
544 "control's width\n"
545 "cc\n"
546 "dd\n"
547 "ee\n"
548 "ff\n"
549 "gg\n"
550 "hh\n";
551 /* The richedit window height needs to be large enough vertically to fit in
552 * more than two lines of text, so the new_richedit function can't be used
553 * since a height of 60 was not large enough on some systems.
554 */
557 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
558 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
559
560 /* Can't verify this */
561 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
562
563 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
564
565 /* Caret above visible window */
566 line_scroll(hwndRichEdit, 3);
567 prevY = get_scroll_pos_y(hwndRichEdit);
568 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
569 curY = get_scroll_pos_y(hwndRichEdit);
570 ok(prevY != curY, "%d == %d\n", prevY, curY);
571
572 /* Caret below visible window */
573 move_cursor(hwndRichEdit, sizeof(text) - 1);
574 line_scroll(hwndRichEdit, -3);
575 prevY = get_scroll_pos_y(hwndRichEdit);
576 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
577 curY = get_scroll_pos_y(hwndRichEdit);
578 ok(prevY != curY, "%d == %d\n", prevY, curY);
579
580 /* Caret in visible window */
581 move_cursor(hwndRichEdit, sizeof(text) - 2);
582 prevY = get_scroll_pos_y(hwndRichEdit);
583 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
584 curY = get_scroll_pos_y(hwndRichEdit);
585 ok(prevY == curY, "%d != %d\n", prevY, curY);
586
587 /* Caret still in visible window */
588 line_scroll(hwndRichEdit, -1);
589 prevY = get_scroll_pos_y(hwndRichEdit);
590 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
591 curY = get_scroll_pos_y(hwndRichEdit);
592 ok(prevY == curY, "%d != %d\n", prevY, curY);
593
594 DestroyWindow(hwndRichEdit);
595}
596
597static void test_EM_POSFROMCHAR(void)
598{
599 HWND hwndRichEdit = new_richedit(NULL);
600 int i, expected;
602 unsigned int height = 0;
603 int xpos = 0;
604 POINTL pt;
605 LOCALESIGNATURE sig;
606 BOOL rtl;
608 static const char text[] = "aa\n"
609 "this is a long line of text that should be longer than the "
610 "control's width\n"
611 "cc\n"
612 "dd\n"
613 "ee\n"
614 "ff\n"
615 "gg\n"
616 "hh\n";
617
619 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
620 (sig.lsUsb[3] & 0x08000000) != 0);
621
622 /* Fill the control to lines to ensure that most of them are offscreen */
623 for (i = 0; i < 50; i++)
624 {
625 /* Do not modify the string; it is exactly 16 characters long. */
626 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
627 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
628 }
629
630 /*
631 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
632 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
633 Richedit 3.0 accepts either of the above API conventions.
634 */
635
636 /* Testing Richedit 2.0 API format */
637
638 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
639 Since all lines are identical and drawn with the same font,
640 they should have the same height... right?
641 */
642 for (i = 0; i < 50; i++)
643 {
644 /* All the lines are 16 characters long */
645 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
646 if (i == 0)
647 {
648 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
649 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
650 xpos = LOWORD(result);
651 }
652 else if (i == 1)
653 {
654 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
655 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
657 }
658 else
659 {
661 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
662 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
663 }
664 }
665
666 /* Testing position at end of text */
667 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
668 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
669 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
670
671 /* Testing position way past end of text */
672 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
673 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
674 expected = (rtl ? 8 : 1);
675 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
676
677 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
678 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
679 for (i = 0; i < 50; i++)
680 {
681 /* All the lines are 16 characters long */
682 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
683 ok((signed short)(HIWORD(result)) == (i - 1) * height,
684 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
685 (signed short)(HIWORD(result)), (i - 1) * height);
686 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
687 }
688
689 /* Testing position at end of text */
690 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
691 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
692 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
693
694 /* Testing position way past end of text */
695 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
696 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
697 expected = (rtl ? 8 : 1);
698 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
699
700 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
701 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
702 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
703
704 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
705 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
706 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
707 xpos = LOWORD(result);
708
709 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
710 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
711 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
712 ok((signed short)(LOWORD(result)) < xpos,
713 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
714 (signed short)(LOWORD(result)), xpos);
715 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
716
717 /* Test around end of text that doesn't end in a newline. */
718 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
719 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
721 ok(pt.x > 1, "pt.x = %d\n", pt.x);
722 xpos = pt.x;
723 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
725 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
726 xpos = (rtl ? pt.x + 7 : pt.x);
727 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
728 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
729 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
730
731 /* Try a negative position. */
732 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
733 ok(pt.x == 1, "pt.x = %d\n", pt.x);
734
735 /* test negative indentation */
736 SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
737 (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
738 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
739 ok(pt.x == 1, "pt.x = %d\n", pt.x);
740
741 fmt.cbSize = sizeof(fmt);
742 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
743 ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
744 ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
745 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
746
747 DestroyWindow(hwndRichEdit);
748}
749
750static void test_EM_SETCHARFORMAT(void)
751{
752 HWND hwndRichEdit = new_richedit(NULL);
753 CHARFORMAT2A cf2;
754 CHARFORMAT2W cfW;
755 CHARFORMATA cf1a;
756 CHARFORMATW cf1w;
757 int rc = 0;
758 int tested_effects[] = {
759 CFE_BOLD,
764 CFE_LINK,
767 0
768 };
769 int i;
770 CHARRANGE cr;
771 LOCALESIGNATURE sig;
772 BOOL rtl;
773 DWORD expect_effects;
774
776 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
777 (sig.lsUsb[3] & 0x08000000) != 0);
778
779 /* check charformat defaults */
780 memset(&cf2, 0, sizeof(CHARFORMAT2A));
781 cf2.cbSize = sizeof(CHARFORMAT2A);
782 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
783 ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
784 expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
785 if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
786 ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
787 ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
788 ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
789 ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
790 ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
791 ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
792 ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
793 ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
794
795 /* Invalid flags, CHARFORMAT2 structure blanked out */
796 memset(&cf2, 0, sizeof(cf2));
797 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
798 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
799
800 /* A valid flag, CHARFORMAT2 structure blanked out */
801 memset(&cf2, 0, sizeof(cf2));
802 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
803 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
804
805 /* A valid flag, CHARFORMAT2 structure blanked out */
806 memset(&cf2, 0, sizeof(cf2));
807 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
808 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
809
810 /* A valid flag, CHARFORMAT2 structure blanked out */
811 memset(&cf2, 0, sizeof(cf2));
812 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
813 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
814
815 /* A valid flag, CHARFORMAT2 structure blanked out */
816 memset(&cf2, 0, sizeof(cf2));
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
818 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
819
820 /* Invalid flags, CHARFORMAT2 structure minimally filled */
821 memset(&cf2, 0, sizeof(cf2));
822 cf2.cbSize = sizeof(CHARFORMAT2A);
823 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
824 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
825 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
826 ok(rc == FALSE, "Should not be able to undo here.\n");
827 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
828
829 /* A valid flag, CHARFORMAT2 structure minimally filled */
830 memset(&cf2, 0, sizeof(cf2));
831 cf2.cbSize = sizeof(CHARFORMAT2A);
832 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
833 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
835 ok(rc == FALSE, "Should not be able to undo here.\n");
836 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
837
838 /* A valid flag, CHARFORMAT2 structure minimally filled */
839 memset(&cf2, 0, sizeof(cf2));
840 cf2.cbSize = sizeof(CHARFORMAT2A);
841 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
842 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
843 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
844 ok(rc == FALSE, "Should not be able to undo here.\n");
845 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
846
847 /* A valid flag, CHARFORMAT2 structure minimally filled */
848 memset(&cf2, 0, sizeof(cf2));
849 cf2.cbSize = sizeof(CHARFORMAT2A);
850 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
851 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
852 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
853 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
854 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
855
856 /* A valid flag, CHARFORMAT2 structure minimally filled */
857 memset(&cf2, 0, sizeof(cf2));
858 cf2.cbSize = sizeof(CHARFORMAT2A);
859 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
860 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
861 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
862 ok(rc == TRUE, "Should not be able to undo here.\n");
863 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
864
865 cf2.cbSize = sizeof(CHARFORMAT2A);
866 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
867
868 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
869 cf2.cbSize = sizeof(CHARFORMAT2A);
870 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
871 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
872 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
873
874 /* wParam==0 is default char format, does not set modify */
875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
876 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
877 ok(rc == 0, "Text marked as modified, expected not modified!\n");
878 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
879 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
880 if (! rtl)
881 {
882 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
883 ok(rc == 0, "Text marked as modified, expected not modified!\n");
884 }
885 else
886 skip("RTL language found\n");
887
888 /* wParam==SCF_SELECTION sets modify if nonempty selection */
889 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
890 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
891 ok(rc == 0, "Text marked as modified, expected not modified!\n");
892 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
893 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
894 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
895 ok(rc == 0, "Text marked as modified, expected not modified!\n");
896
897 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
899 ok(rc == 0, "Text marked as modified, expected not modified!\n");
900 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
902 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
903 ok(rc == 0, "Text marked as modified, expected not modified!\n");
904 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
905 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
906 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
907 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
908 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
909
910 /* wParam==SCF_ALL sets modify regardless of whether text is present */
911 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
912 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
913 ok(rc == 0, "Text marked as modified, expected not modified!\n");
914 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
915 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
916 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
917 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
918
919 DestroyWindow(hwndRichEdit);
920
921 /* EM_GETCHARFORMAT tests */
922 for (i = 0; tested_effects[i]; i++)
923 {
924 hwndRichEdit = new_richedit(NULL);
925 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
926
927 /* Need to set a TrueType font to get consistent CFM_BOLD results */
928 memset(&cf2, 0, sizeof(CHARFORMAT2A));
929 cf2.cbSize = sizeof(CHARFORMAT2A);
931 cf2.dwEffects = 0;
932 strcpy(cf2.szFaceName, "Courier New");
933 cf2.wWeight = FW_DONTCARE;
934 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
935
936 memset(&cf2, 0, sizeof(CHARFORMAT2A));
937 cf2.cbSize = sizeof(CHARFORMAT2A);
938 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
939 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
940 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
941 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
942 ||
943 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
944 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
945 ok((cf2.dwEffects & tested_effects[i]) == 0,
946 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
947
948 memset(&cf2, 0, sizeof(CHARFORMAT2A));
949 cf2.cbSize = sizeof(CHARFORMAT2A);
950 cf2.dwMask = tested_effects[i];
951 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952 cf2.dwMask = CFM_SUPERSCRIPT;
953 cf2.dwEffects = tested_effects[i];
954 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
955 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
956
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
963 ||
964 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
967 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
968
969 memset(&cf2, 0, sizeof(CHARFORMAT2A));
970 cf2.cbSize = sizeof(CHARFORMAT2A);
971 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975 ||
976 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978 ok((cf2.dwEffects & tested_effects[i]) == 0,
979 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980
981 memset(&cf2, 0, sizeof(CHARFORMAT2A));
982 cf2.cbSize = sizeof(CHARFORMAT2A);
983 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
987 ||
988 (cf2.dwMask & tested_effects[i]) == 0),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990
991 DestroyWindow(hwndRichEdit);
992 }
993
994 for (i = 0; tested_effects[i]; i++)
995 {
996 hwndRichEdit = new_richedit(NULL);
997 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
998
999 /* Need to set a TrueType font to get consistent CFM_BOLD results */
1000 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1001 cf2.cbSize = sizeof(CHARFORMAT2A);
1003 cf2.dwEffects = 0;
1004 strcpy(cf2.szFaceName, "Courier New");
1005 cf2.wWeight = FW_DONTCARE;
1006 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1007
1008 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1009 cf2.cbSize = sizeof(CHARFORMAT2A);
1010 cf2.dwMask = tested_effects[i];
1011 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1012 cf2.dwMask = CFM_SUPERSCRIPT;
1013 cf2.dwEffects = tested_effects[i];
1014 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1015 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016
1017 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018 cf2.cbSize = sizeof(CHARFORMAT2A);
1019 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1020 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1023 ||
1024 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1025 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1026 ok((cf2.dwEffects & tested_effects[i]) == 0,
1027 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1028
1029 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1030 cf2.cbSize = sizeof(CHARFORMAT2A);
1031 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1032 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1033 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1034 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1035 ||
1036 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1037 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1038 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1039 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1040
1041 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1042 cf2.cbSize = sizeof(CHARFORMAT2A);
1043 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1044 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1045 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1046 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1047 ||
1048 (cf2.dwMask & tested_effects[i]) == 0),
1049 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1050 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1051 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1052
1053 DestroyWindow(hwndRichEdit);
1054 }
1055
1056 /* Effects applied on an empty selection should take effect when selection is
1057 replaced with text */
1058 hwndRichEdit = new_richedit(NULL);
1059 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1061
1062 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063 cf2.cbSize = sizeof(CHARFORMAT2A);
1064 cf2.dwMask = CFM_BOLD;
1065 cf2.dwEffects = CFE_BOLD;
1066 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067
1068 /* Selection is now nonempty */
1069 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1070
1071 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1072 cf2.cbSize = sizeof(CHARFORMAT2A);
1073 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1074 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1075
1076 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1077 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1078 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1079 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1080
1081
1082 /* Set two effects on an empty selection */
1083 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1084 /* first clear bold, italic */
1085 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1086 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1087 cf2.cbSize = sizeof(CHARFORMAT2A);
1088 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1089 cf2.dwEffects = 0;
1090 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1091
1092 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1093
1094 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1095 cf2.cbSize = sizeof(CHARFORMAT2A);
1096 cf2.dwMask = CFM_BOLD;
1097 cf2.dwEffects = CFE_BOLD;
1098 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099 cf2.dwMask = CFM_ITALIC;
1100 cf2.dwEffects = CFE_ITALIC;
1101 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1102
1103 /* Selection is now nonempty */
1104 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1105
1106 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1107 cf2.cbSize = sizeof(CHARFORMAT2A);
1108 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1109 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1110
1111 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1112 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1114 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1115
1116 /* Setting the (empty) selection to exactly the same place as before should
1117 NOT clear the insertion style! */
1118 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1119 /* first clear bold, italic */
1120 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1121 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1122 cf2.cbSize = sizeof(CHARFORMAT2A);
1123 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1124 cf2.dwEffects = 0;
1125 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1126
1127 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1128
1129 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130 cf2.cbSize = sizeof(CHARFORMAT2A);
1131 cf2.dwMask = CFM_BOLD;
1132 cf2.dwEffects = CFE_BOLD;
1133 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1134
1135 /* Empty selection in same place, insert style should NOT be forgotten here. */
1136 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1137
1138 /* Selection is now nonempty */
1139 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1140
1141 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1142 cf2.cbSize = sizeof(CHARFORMAT2A);
1143 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1144 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1145
1146 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1147 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1148 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1149 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1150
1151 /* Moving the selection will clear the insertion style */
1152 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1153 /* first clear bold, italic */
1154 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1155 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1156 cf2.cbSize = sizeof(CHARFORMAT2A);
1157 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1158 cf2.dwEffects = 0;
1159 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1160
1161 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1162
1163 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1164 cf2.cbSize = sizeof(CHARFORMAT2A);
1165 cf2.dwMask = CFM_BOLD;
1166 cf2.dwEffects = CFE_BOLD;
1167 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1168
1169 /* Move selection and then put it back, insert style should be forgotten here. */
1170 SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1171 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1172
1173 /* Selection is now nonempty */
1174 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1175
1176 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1177 cf2.cbSize = sizeof(CHARFORMAT2A);
1178 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1179 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1180
1181 ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1182 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1183 ok((cf2.dwEffects & CFE_BOLD) == 0,
1184 "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1185
1186 /* Ditto with EM_EXSETSEL */
1187 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1188 /* first clear bold, italic */
1189 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1190 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1191 cf2.cbSize = sizeof(CHARFORMAT2A);
1192 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1193 cf2.dwEffects = 0;
1194 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1195
1196 cr.cpMin = 2; cr.cpMax = 2;
1197 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1198
1199 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1200 cf2.cbSize = sizeof(CHARFORMAT2A);
1201 cf2.dwMask = CFM_BOLD;
1202 cf2.dwEffects = CFE_BOLD;
1203 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1204
1205 /* Empty selection in same place, insert style should NOT be forgotten here. */
1206 cr.cpMin = 2; cr.cpMax = 2;
1207 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1208
1209 /* Selection is now nonempty */
1210 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1211
1212 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1213 cf2.cbSize = sizeof(CHARFORMAT2A);
1214 cr.cpMin = 2; cr.cpMax = 6;
1215 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1216 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1217
1218 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1219 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1220 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1221 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1222
1223 /* show that wWeight is at the correct offset in CHARFORMAT2A */
1224 memset(&cf2, 0, sizeof(cf2));
1225 cf2.cbSize = sizeof(cf2);
1226 cf2.dwMask = CFM_WEIGHT;
1227 cf2.wWeight = 100;
1228 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1229 memset(&cf2, 0, sizeof(cf2));
1230 cf2.cbSize = sizeof(cf2);
1231 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1232 ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1233
1234 memset(&cf2, 0, sizeof(cf2));
1235 cf2.cbSize = sizeof(cf2);
1236 cf2.dwMask = CFM_SPACING;
1237 cf2.sSpacing = 10;
1238 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1239 memset(&cf2, 0, sizeof(cf2));
1240 cf2.cbSize = sizeof(cf2);
1241 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1242 ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1243
1244 /* show that wWeight is at the correct offset in CHARFORMAT2W */
1245 memset(&cfW, 0, sizeof(cfW));
1246 cfW.cbSize = sizeof(cfW);
1247 cfW.dwMask = CFM_WEIGHT;
1248 cfW.wWeight = 100;
1249 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1250 memset(&cfW, 0, sizeof(cfW));
1251 cfW.cbSize = sizeof(cfW);
1252 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1253 ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1254
1255 memset(&cfW, 0, sizeof(cfW));
1256 cfW.cbSize = sizeof(cfW);
1257 cfW.dwMask = CFM_SPACING;
1258 cfW.sSpacing = 10;
1259 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1260 memset(&cfW, 0, sizeof(cfW));
1261 cfW.cbSize = sizeof(cfW);
1262 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1263 ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1264
1265 /* test CFE_UNDERLINE and bUnderlineType interaction */
1266 /* clear bold, italic */
1267 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1268 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1269 cf2.cbSize = sizeof(CHARFORMAT2A);
1270 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1271 cf2.dwEffects = 0;
1272 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273
1274 /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1275 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1276 cf2.cbSize = sizeof(CHARFORMAT2A);
1277 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1279 "got %08x\n", cf2.dwMask);
1280 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1281 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1282
1283 /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1286 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1287 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1288 cf2.cbSize = sizeof(CHARFORMAT2A);
1289 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1291 "got %08x\n", cf2.dwMask);
1292 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1293 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1294
1295 /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1298 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1299 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1300 cf2.cbSize = sizeof(CHARFORMAT2A);
1301 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1303 "got %08x\n", cf2.dwMask);
1304 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1305 ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1306
1307 /* another underline type also sets CFE_UNDERLINE */
1310 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1311 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1312 cf2.cbSize = sizeof(CHARFORMAT2A);
1313 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1315 "got %08x\n", cf2.dwMask);
1316 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1317 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1318
1319 /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1322 cf2.dwEffects = 0;
1323 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1324 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1325 cf2.cbSize = sizeof(CHARFORMAT2A);
1326 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1328 "got %08x\n", cf2.dwMask);
1329 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1330 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1331
1332 /* And turing it back on again by just setting CFE_UNDERLINE */
1333 cf2.dwMask = CFM_UNDERLINE;
1335 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1336 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1337 cf2.cbSize = sizeof(CHARFORMAT2A);
1338 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1340 "got %08x\n", cf2.dwMask);
1341 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1342 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1343
1344 /* Check setting CFM_ALL2/CFM_EFFECTS2 in CHARFORMAT(A/W). */
1345 memset(&cf1a, 0, sizeof(CHARFORMATA));
1346 memset(&cf1w, 0, sizeof(CHARFORMATW));
1347 cf1a.cbSize = sizeof(CHARFORMATA);
1348 cf1w.cbSize = sizeof(CHARFORMATW);
1349 cf1a.dwMask = cf1w.dwMask = CFM_ALL2;
1350 cf1a.dwEffects = cf1w.dwEffects = CFM_EFFECTS2;
1351 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1352 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1353 /* flags only valid for CHARFORMAT2 should be masked out */
1354 ok((cf1a.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1355 ok((cf1a.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1356 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1357 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1358 ok((cf1w.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1359 ok((cf1w.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1360
1361 DestroyWindow(hwndRichEdit);
1362}
1363
1364static void test_EM_SETTEXTMODE(void)
1365{
1366 HWND hwndRichEdit = new_richedit(NULL);
1367 CHARFORMAT2A cf2, cf2test;
1368 CHARRANGE cr;
1369 int rc = 0;
1370
1371 /*Attempt to use mutually exclusive modes*/
1373 ok(rc == E_INVALIDARG,
1374 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1375
1376 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1377 /*Insert text into the control*/
1378
1379 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1380
1381 /*Attempt to change the control to plain text mode*/
1382 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1383 ok(rc == E_UNEXPECTED,
1384 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1385
1386 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1387 If rich text is pasted, it should have the same formatting as the rest
1388 of the text in the control*/
1389
1390 /*Italicize the text
1391 *NOTE: If the default text was already italicized, the test will simply
1392 reverse; in other words, it will copy a regular "wine" into a plain
1393 text window that uses an italicized format*/
1394 cf2.cbSize = sizeof(CHARFORMAT2A);
1395 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1396
1397 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1398 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1399
1400 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1401 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1402
1403 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1404 however, SCF_ALL has been implemented*/
1405 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1406 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1407
1408 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1409 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1410
1411 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1412
1413 /*Select the string "wine"*/
1414 cr.cpMin = 0;
1415 cr.cpMax = 4;
1416 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1417
1418 /*Copy the italicized "wine" to the clipboard*/
1419 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1420
1421 /*Reset the formatting to default*/
1422 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1423 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1424 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1425
1426 /*Clear the text in the control*/
1427 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1428
1429 /*Switch to Plain Text Mode*/
1430 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1431 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1432
1433 /*Input "wine" again in normal format*/
1434 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1435
1436 /*Paste the italicized "wine" into the control*/
1437 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1438
1439 /*Select a character from the first "wine" string*/
1440 cr.cpMin = 2;
1441 cr.cpMax = 3;
1442 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1443
1444 /*Retrieve its formatting*/
1446
1447 /*Select a character from the second "wine" string*/
1448 cr.cpMin = 5;
1449 cr.cpMax = 6;
1450 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1451
1452 /*Retrieve its formatting*/
1453 cf2test.cbSize = sizeof(CHARFORMAT2A);
1454 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1455
1456 /*Compare the two formattings*/
1457 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1458 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1459 cf2.dwEffects, cf2test.dwEffects);
1460 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1461 printing "wine" in the current format(normal)
1462 pasting "wine" from the clipboard(italicized)
1463 comparing the two formats(should differ)*/
1464
1465 /*Attempt to switch with text in control*/
1466 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1467 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1468
1469 /*Clear control*/
1470 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1471
1472 /*Switch into Rich Text mode*/
1473 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1474 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1475
1476 /*Print "wine" in normal formatting into the control*/
1477 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1478
1479 /*Paste italicized "wine" into the control*/
1480 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1481
1482 /*Select text from the first "wine" string*/
1483 cr.cpMin = 1;
1484 cr.cpMax = 3;
1485 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1486
1487 /*Retrieve its formatting*/
1489
1490 /*Select text from the second "wine" string*/
1491 cr.cpMin = 6;
1492 cr.cpMax = 7;
1493 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1494
1495 /*Retrieve its formatting*/
1496 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1497
1498 /*Test that the two formattings are not the same*/
1499 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1500 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1501 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1502
1503 DestroyWindow(hwndRichEdit);
1504}
1505
1506static void test_SETPARAFORMAT(void)
1507{
1508 HWND hwndRichEdit = new_richedit(NULL);
1510 HRESULT ret;
1511 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1512 fmt.cbSize = sizeof(PARAFORMAT2);
1513 fmt.dwMask = PFM_ALIGNMENT;
1514 fmt.wAlignment = PFA_LEFT;
1515
1516 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1517 ok(ret != 0, "expected non-zero got %d\n", ret);
1518
1519 fmt.cbSize = sizeof(PARAFORMAT2);
1520 fmt.dwMask = -1;
1521 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1522 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1523 * between richedit different native builds of riched20.dll
1524 * used on different Windows versions. */
1525 ret &= ~PFM_TABLEROWDELIMITER;
1526 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1527
1528 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1529 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1530
1531 /* Test some other paraformat field defaults */
1532 ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1533 ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1534 ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1535 ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1536
1537 DestroyWindow(hwndRichEdit);
1538}
1539
1540static void test_TM_PLAINTEXT(void)
1541{
1542 /*Tests plain text properties*/
1543
1544 HWND hwndRichEdit = new_richedit(NULL);
1545 CHARFORMAT2A cf2, cf2test;
1546 CHARRANGE cr;
1547 int rc = 0;
1548
1549 /*Switch to plain text mode*/
1550
1551 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1552 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1553
1554 /*Fill control with text*/
1555
1556 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1557
1558 /*Select some text and bold it*/
1559
1560 cr.cpMin = 10;
1561 cr.cpMax = 20;
1562 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1563 cf2.cbSize = sizeof(CHARFORMAT2A);
1564 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1565
1566 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1567 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1568
1569 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1570 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1571
1572 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1573 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1574
1575 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1576 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1577
1578 /*Get the formatting of those characters*/
1579
1580 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1581
1582 /*Get the formatting of some other characters*/
1583 cf2test.cbSize = sizeof(CHARFORMAT2A);
1584 cr.cpMin = 21;
1585 cr.cpMax = 30;
1586 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1587 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1588
1589 /*Test that they are the same as plain text allows only one formatting*/
1590
1591 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1592 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1593 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1594
1595 /*Fill the control with a "wine" string, which when inserted will be bold*/
1596
1597 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1598
1599 /*Copy the bolded "wine" string*/
1600
1601 cr.cpMin = 0;
1602 cr.cpMax = 4;
1603 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1604 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1605
1606 /*Swap back to rich text*/
1607
1608 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1609 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1610
1611 /*Set the default formatting to bold italics*/
1612
1613 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1614 cf2.dwMask |= CFM_ITALIC;
1615 cf2.dwEffects ^= CFE_ITALIC;
1616 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1617 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1618
1619 /*Set the text in the control to "wine", which will be bold and italicized*/
1620
1621 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1622
1623 /*Paste the plain text "wine" string, which should take the insert
1624 formatting, which at the moment is bold italics*/
1625
1626 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1627
1628 /*Select the first "wine" string and retrieve its formatting*/
1629
1630 cr.cpMin = 1;
1631 cr.cpMax = 3;
1632 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1633 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1634
1635 /*Select the second "wine" string and retrieve its formatting*/
1636
1637 cr.cpMin = 5;
1638 cr.cpMax = 7;
1639 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1640 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1641
1642 /*Compare the two formattings. They should be the same.*/
1643
1644 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1645 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1646 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1647 DestroyWindow(hwndRichEdit);
1648}
1649
1650static void test_WM_GETTEXT(void)
1651{
1652 HWND hwndRichEdit = new_richedit(NULL);
1653 static const char text[] = "Hello. My name is RichEdit!";
1654 static const char text2[] = "Hello. My name is RichEdit!\r";
1655 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1656 char buffer[1024] = {0};
1657 int result;
1658
1659 /* Baseline test with normal-sized buffer */
1660 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1661 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1663 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1664 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1666 ok(result == 0,
1667 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1668
1669 /* Test for returned value of WM_GETTEXTLENGTH */
1670 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1671 ok(result == lstrlenA(text),
1672 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1673 result, lstrlenA(text));
1674
1675 /* Test for behavior in overflow case */
1676 memset(buffer, 0, 1024);
1677 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1678 ok(result == 0 ||
1679 result == lstrlenA(text) - 1, /* XP, win2k3 */
1680 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1682 if (result)
1683 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1684 ok(result == 0,
1685 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1686
1687 /* Baseline test with normal-sized buffer and carriage return */
1688 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1689 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1691 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1692 result = strcmp(buffer,text2_after);
1693 ok(result == 0,
1694 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1695
1696 /* Test for returned value of WM_GETTEXTLENGTH */
1697 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1698 ok(result == lstrlenA(text2_after),
1699 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1700 result, lstrlenA(text2_after));
1701
1702 /* Test for behavior of CRLF conversion in case of overflow */
1703 memset(buffer, 0, 1024);
1704 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1705 ok(result == 0 ||
1706 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1707 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1708 result = strcmp(buffer,text2);
1709 if (result)
1710 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1711 ok(result == 0,
1712 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1713
1714 DestroyWindow(hwndRichEdit);
1715}
1716
1717static void test_EM_GETTEXTRANGE(void)
1718{
1719 HWND hwndRichEdit = new_richedit(NULL);
1720 const char * text1 = "foo bar\r\nfoo bar";
1721 const char * text2 = "foo bar\rfoo bar";
1722 const char * expect = "bar\rfoo";
1723 char buffer[1024] = {0};
1725 TEXTRANGEA textRange;
1726
1727 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1728
1729 textRange.lpstrText = buffer;
1730 textRange.chrg.cpMin = 4;
1731 textRange.chrg.cpMax = 11;
1732 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1733 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1734 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1735
1736 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1737
1738 textRange.lpstrText = buffer;
1739 textRange.chrg.cpMin = 4;
1740 textRange.chrg.cpMax = 11;
1741 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1742 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1743 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1744
1745 /* cpMax of text length is used instead of -1 in this case */
1746 textRange.lpstrText = buffer;
1747 textRange.chrg.cpMin = 0;
1748 textRange.chrg.cpMax = -1;
1749 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1750 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1751 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1752
1753 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1754 textRange.lpstrText = buffer;
1755 textRange.chrg.cpMin = -1;
1756 textRange.chrg.cpMax = 1;
1757 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1758 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1759 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1760
1761 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1762 textRange.lpstrText = buffer;
1763 textRange.chrg.cpMin = 1;
1764 textRange.chrg.cpMax = -1;
1765 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1766 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1767 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1768
1769 /* no end character is copied if cpMax - cpMin < 0 */
1770 textRange.lpstrText = buffer;
1771 textRange.chrg.cpMin = 5;
1772 textRange.chrg.cpMax = 5;
1773 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1774 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1775 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1776
1777 /* cpMax of text length is used if cpMax > text length*/
1778 textRange.lpstrText = buffer;
1779 textRange.chrg.cpMin = 0;
1780 textRange.chrg.cpMax = 1000;
1781 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1782 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1783 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1784
1785 /* Test with multibyte character */
1786 if (!is_lang_japanese)
1787 skip("Skip multibyte character tests on non-Japanese platform\n");
1788 else
1789 {
1790 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1791 textRange.chrg.cpMin = 4;
1792 textRange.chrg.cpMax = 8;
1793 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1794 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1795 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1796 }
1797
1798 DestroyWindow(hwndRichEdit);
1799}
1800
1801static void test_EM_GETSELTEXT(void)
1802{
1803 HWND hwndRichEdit = new_richedit(NULL);
1804 const char * text1 = "foo bar\r\nfoo bar";
1805 const char * text2 = "foo bar\rfoo bar";
1806 const char * expect = "bar\rfoo";
1807 char buffer[1024] = {0};
1809
1810 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1811
1812 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1813 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1814 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1815 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1816
1817 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1818
1819 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1820 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1821 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1822 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1823
1824 /* Test with multibyte character */
1825 if (!is_lang_japanese)
1826 skip("Skip multibyte character tests on non-Japanese platform\n");
1827 else
1828 {
1829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1830 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1831 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1832 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1833 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1834 }
1835
1836 DestroyWindow(hwndRichEdit);
1837}
1838
1839/* FIXME: need to test unimplemented options and robustly test wparam */
1840static void test_EM_SETOPTIONS(void)
1841{
1842 HWND hwndRichEdit;
1843 static const char text[] = "Hello. My name is RichEdit!";
1844 char buffer[1024] = {0};
1845 DWORD dwStyle, options, oldOptions;
1849
1850 /* Test initial options. */
1852 0, 0, 200, 60, NULL, NULL,
1854 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1856 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1857 ok(options == 0, "Incorrect initial options %x\n", options);
1858 DestroyWindow(hwndRichEdit);
1859
1860 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1862 0, 0, 200, 60, NULL, NULL,
1864 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1866 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1867 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1869 "Incorrect initial options %x\n", options);
1870
1871 /* NEGATIVE TESTING - NO OPTIONS SET */
1872 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1873 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1874
1875 /* testing no readonly by sending 'a' to the control*/
1876 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1877 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1878 ok(buffer[0]=='a',
1879 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1880 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1881
1882 /* READONLY - sending 'a' to the control */
1883 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1885 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1886 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1887 ok(buffer[0]==text[0],
1888 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1889
1890 /* EM_SETOPTIONS changes the window style, but changing the
1891 * window style does not change the options. */
1892 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1893 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1894 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1895 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1896 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1897 /* Confirm that the text is still read only. */
1898 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1899 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1900 ok(buffer[0]==text[0],
1901 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1902
1903 oldOptions = options;
1904 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1905 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1906 ok(options == oldOptions,
1907 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1908
1909 DestroyWindow(hwndRichEdit);
1910}
1911
1912static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1913{
1914 CHARFORMAT2A text_format;
1915 text_format.cbSize = sizeof(text_format);
1916 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1918 return (text_format.dwEffects & CFE_LINK) != 0;
1919}
1920
1921static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1922{
1923 BOOL link_present = FALSE;
1924
1925 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1926 if (is_url)
1927 { /* control text is url; should get CFE_LINK */
1928 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1929 }
1930 else
1931 {
1932 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1933 }
1934}
1935
1937 return new_window("Static", 0, parent);
1938}
1939
1940static void test_EM_AUTOURLDETECT(void)
1941{
1942 /* DO NOT change the properties of the first two elements. To shorten the
1943 tests, all tests after WM_SETTEXT test just the first two elements -
1944 one non-URL and one URL */
1945 struct urls_s {
1946 const char *text;
1947 BOOL is_url;
1948 } urls[12] = {
1949 {"winehq.org", FALSE},
1950 {"http://www.winehq.org", TRUE},
1951 {"http//winehq.org", FALSE},
1952 {"ww.winehq.org", FALSE},
1953 {"www.winehq.org", TRUE},
1954 {"ftp://192.168.1.1", TRUE},
1955 {"ftp//192.168.1.1", FALSE},
1956 {"mailto:your@email.com", TRUE},
1957 {"prospero:prosperoserver", TRUE},
1958 {"telnet:test", TRUE},
1959 {"news:newserver", TRUE},
1960 {"wais:waisserver", TRUE}
1961 };
1962
1963 int i, j;
1964 int urlRet=-1;
1965 HWND hwndRichEdit, parent;
1966
1967 /* All of the following should cause the URL to be detected */
1968 const char * templates_delim[] = {
1969 "This is some text with X on it",
1970 "This is some text with (X) on it",
1971 "This is some text with X\r on it",
1972 "This is some text with ---X--- on it",
1973 "This is some text with \"X\" on it",
1974 "This is some text with 'X' on it",
1975 "This is some text with 'X' on it",
1976 "This is some text with :X: on it",
1977
1978 "This text ends with X",
1979
1980 "This is some text with X) on it",
1981 "This is some text with X--- on it",
1982 "This is some text with X\" on it",
1983 "This is some text with X' on it",
1984 "This is some text with X: on it",
1985
1986 "This is some text with (X on it",
1987 "This is some text with \rX on it",
1988 "This is some text with ---X on it",
1989 "This is some text with \"X on it",
1990 "This is some text with 'X on it",
1991 "This is some text with :X on it",
1992 };
1993 /* None of these should cause the URL to be detected */
1994 const char * templates_non_delim[] = {
1995 "This is some text with |X| on it",
1996 "This is some text with *X* on it",
1997 "This is some text with /X/ on it",
1998 "This is some text with +X+ on it",
1999 "This is some text with %X% on it",
2000 "This is some text with #X# on it",
2001 "This is some text with @X@ on it",
2002 "This is some text with \\X\\ on it",
2003 "This is some text with |X on it",
2004 "This is some text with *X on it",
2005 "This is some text with /X on it",
2006 "This is some text with +X on it",
2007 "This is some text with %X on it",
2008 "This is some text with #X on it",
2009 "This is some text with @X on it",
2010 "This is some text with \\X on it",
2011 "This is some text with _X on it",
2012 };
2013 /* All of these cause the URL detection to be extended by one more byte,
2014 thus demonstrating that the tested character is considered as part
2015 of the URL. */
2016 const char * templates_xten_delim[] = {
2017 "This is some text with X| on it",
2018 "This is some text with X* on it",
2019 "This is some text with X/ on it",
2020 "This is some text with X+ on it",
2021 "This is some text with X% on it",
2022 "This is some text with X# on it",
2023 "This is some text with X@ on it",
2024 "This is some text with X\\ on it",
2025 "This is some text with X_ on it",
2026 };
2027 /* These delims act as neutral breaks. Whether the url is ended
2028 or not depends on the next non-neutral character. We'll test
2029 with Y unchanged, in which case the url should include the
2030 deliminator and the Y. We'll also test with the Y changed
2031 to a space, in which case the url stops before the
2032 deliminator. */
2033 const char * templates_neutral_delim[] = {
2034 "This is some text with X-Y on it",
2035 "This is some text with X--Y on it",
2036 "This is some text with X!Y on it",
2037 "This is some text with X[Y on it",
2038 "This is some text with X]Y on it",
2039 "This is some text with X{Y on it",
2040 "This is some text with X}Y on it",
2041 "This is some text with X(Y on it",
2042 "This is some text with X)Y on it",
2043 "This is some text with X\"Y on it",
2044 "This is some text with X;Y on it",
2045 "This is some text with X:Y on it",
2046 "This is some text with X'Y on it",
2047 "This is some text with X?Y on it",
2048 "This is some text with X<Y on it",
2049 "This is some text with X>Y on it",
2050 "This is some text with X.Y on it",
2051 "This is some text with X,Y on it",
2052 };
2053 char buffer[1024];
2054
2056 hwndRichEdit = new_richedit(parent);
2057 /* Try and pass EM_AUTOURLDETECT some test wParam values */
2058 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2059 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2060 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2061 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2062 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2063 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2064 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2065 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2066 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2067 /* for each url, check the text to see if CFE_LINK effect is present */
2068 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2069
2070 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2071 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2072 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2073
2074 /* Link detection should happen immediately upon WM_SETTEXT */
2075 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2077 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2078 }
2079 DestroyWindow(hwndRichEdit);
2080
2081 /* Test detection of URLs within normal text - WM_SETTEXT case. */
2082 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2083 hwndRichEdit = new_richedit(parent);
2084
2085 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2086 char * at_pos;
2087 int at_offset;
2088 int end_offset;
2089
2090 at_pos = strchr(templates_delim[j], 'X');
2091 at_offset = at_pos - templates_delim[j];
2092 memcpy(buffer, templates_delim[j], at_offset);
2093 buffer[at_offset] = '\0';
2094 strcat(buffer, urls[i].text);
2095 strcat(buffer, templates_delim[j] + at_offset + 1);
2096 end_offset = at_offset + strlen(urls[i].text);
2097
2098 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2099 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2100
2101 /* This assumes no templates start with the URL itself, and that they
2102 have at least two characters before the URL text */
2103 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2104 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2105 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2106 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2107 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2108 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2109
2110 if (urls[i].is_url)
2111 {
2112 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2114 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2116 }
2117 else
2118 {
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2121 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2122 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2123 }
2124 if (buffer[end_offset] != '\0')
2125 {
2126 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2127 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2128 if (buffer[end_offset +1] != '\0')
2129 {
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2132 }
2133 }
2134 }
2135
2136 for (j = 0; j < ARRAY_SIZE(templates_non_delim); j++) {
2137 char * at_pos;
2138 int at_offset;
2139 int end_offset;
2140
2141 at_pos = strchr(templates_non_delim[j], 'X');
2142 at_offset = at_pos - templates_non_delim[j];
2143 memcpy(buffer, templates_non_delim[j], at_offset);
2144 buffer[at_offset] = '\0';
2145 strcat(buffer, urls[i].text);
2146 strcat(buffer, templates_non_delim[j] + at_offset + 1);
2147 end_offset = at_offset + strlen(urls[i].text);
2148
2149 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2150 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2151
2152 /* This assumes no templates start with the URL itself, and that they
2153 have at least two characters before the URL text */
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2158 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2159 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2160
2161 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2163 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165 if (buffer[end_offset] != '\0')
2166 {
2167 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2168 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2169 if (buffer[end_offset +1] != '\0')
2170 {
2171 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2172 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2173 }
2174 }
2175 }
2176
2177 for (j = 0; j < ARRAY_SIZE(templates_xten_delim); j++) {
2178 char * at_pos;
2179 int at_offset;
2180 int end_offset;
2181
2182 at_pos = strchr(templates_xten_delim[j], 'X');
2183 at_offset = at_pos - templates_xten_delim[j];
2184 memcpy(buffer, templates_xten_delim[j], at_offset);
2185 buffer[at_offset] = '\0';
2186 strcat(buffer, urls[i].text);
2187 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2188 end_offset = at_offset + strlen(urls[i].text);
2189
2190 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2191 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2192
2193 /* This assumes no templates start with the URL itself, and that they
2194 have at least two characters before the URL text */
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2197 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2198 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2201
2202 if (urls[i].is_url)
2203 {
2204 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2206 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2208 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2209 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2210 }
2211 else
2212 {
2213 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2214 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2215 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2216 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2217 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2218 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2219 }
2220 if (buffer[end_offset +1] != '\0')
2221 {
2222 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2223 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2224 if (buffer[end_offset +2] != '\0')
2225 {
2226 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2227 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2228 }
2229 }
2230 }
2231
2232 for (j = 0; j < ARRAY_SIZE(templates_neutral_delim); j++) {
2233 char * at_pos, * end_pos;
2234 int at_offset;
2235 int end_offset;
2236
2237 if (!urls[i].is_url) continue;
2238
2239 at_pos = strchr(templates_neutral_delim[j], 'X');
2240 at_offset = at_pos - templates_neutral_delim[j];
2241 memcpy(buffer, templates_neutral_delim[j], at_offset);
2242 buffer[at_offset] = '\0';
2243 strcat(buffer, urls[i].text);
2244 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2245
2246 end_pos = strchr(buffer, 'Y');
2247 end_offset = end_pos - buffer;
2248
2249 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2250 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2251
2252 /* This assumes no templates start with the URL itself, and that they
2253 have at least two characters before the URL text */
2254 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2255 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2256 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2257 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2258 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2259 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2260
2261 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2262 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2263 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2264 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2265 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2266 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2267
2268 *end_pos = ' ';
2269
2270 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2271 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2272
2273 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2274 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2275 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2276 "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2277 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2278 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2279 }
2280
2281 DestroyWindow(hwndRichEdit);
2282 hwndRichEdit = NULL;
2283 }
2284
2285 /* Test detection of URLs within normal text - WM_CHAR case. */
2286 /* Test only the first two URL examples for brevity */
2287 for (i = 0; i < 2; i++) {
2288 hwndRichEdit = new_richedit(parent);
2289
2290 /* Also for brevity, test only the first three delimiters */
2291 for (j = 0; j < 3; j++) {
2292 char * at_pos;
2293 int at_offset;
2294 int end_offset;
2295 int u, v;
2296
2297 at_pos = strchr(templates_delim[j], 'X');
2298 at_offset = at_pos - templates_delim[j];
2299 end_offset = at_offset + strlen(urls[i].text);
2300
2301 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2302 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2303 for (u = 0; templates_delim[j][u]; u++) {
2304 if (templates_delim[j][u] == '\r') {
2305 simulate_typing_characters(hwndRichEdit, "\r");
2306 } else if (templates_delim[j][u] != 'X') {
2307 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2308 } else {
2309 for (v = 0; urls[i].text[v]; v++) {
2310 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2311 }
2312 }
2313 }
2314 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2315
2316 /* This assumes no templates start with the URL itself, and that they
2317 have at least two characters before the URL text */
2318 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2319 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2320 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2321 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2322 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2323 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2324
2325 if (urls[i].is_url)
2326 {
2327 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2328 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2329 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2330 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2331 }
2332 else
2333 {
2334 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2336 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2338 }
2339 if (buffer[end_offset] != '\0')
2340 {
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2343 if (buffer[end_offset +1] != '\0')
2344 {
2345 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2346 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2347 }
2348 }
2349
2350 /* The following will insert a paragraph break after the first character
2351 of the URL candidate, thus breaking the URL. It is expected that the
2352 CFE_LINK attribute should break across both pieces of the URL */
2353 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2354 simulate_typing_characters(hwndRichEdit, "\r");
2355 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2356
2357 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2358 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2359 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2360 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2361 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2362 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2363
2364 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2365 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2366 /* end_offset moved because of paragraph break */
2367 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2368 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2369 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2370 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2371 {
2372 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2373 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2374 if (buffer[end_offset +2] != '\0')
2375 {
2376 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2377 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2378 }
2379 }
2380
2381 /* The following will remove the just-inserted paragraph break, thus
2382 restoring the URL */
2383 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2384 simulate_typing_characters(hwndRichEdit, "\b");
2385 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2386
2387 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2388 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2389 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2390 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2391 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2392 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2393
2394 if (urls[i].is_url)
2395 {
2396 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2397 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2398 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2399 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2400 }
2401 else
2402 {
2403 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2404 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2405 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2406 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2407 }
2408 if (buffer[end_offset] != '\0')
2409 {
2410 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2411 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2412 if (buffer[end_offset +1] != '\0')
2413 {
2414 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2415 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2416 }
2417 }
2418 }
2419 DestroyWindow(hwndRichEdit);
2420 hwndRichEdit = NULL;
2421 }
2422
2423 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2424 /* Test just the first two URL examples for brevity */
2425 for (i = 0; i < 2; i++) {
2426 SETTEXTEX st;
2427
2428 hwndRichEdit = new_richedit(parent);
2429
2430 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2431 be detected:
2432 1) Set entire text, a la WM_SETTEXT
2433 2) Set a selection of the text to the URL
2434 3) Set a portion of the text at a time, which eventually results in
2435 an URL
2436 All of them should give equivalent results
2437 */
2438
2439 /* Set entire text in one go, like WM_SETTEXT */
2440 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2441 char * at_pos;
2442 int at_offset;
2443 int end_offset;
2444
2445 st.codepage = CP_ACP;
2446 st.flags = ST_DEFAULT;
2447
2448 at_pos = strchr(templates_delim[j], 'X');
2449 at_offset = at_pos - templates_delim[j];
2450 memcpy(buffer, templates_delim[j], at_offset);
2451 buffer[at_offset] = '\0';
2452 strcat(buffer, urls[i].text);
2453 strcat(buffer, templates_delim[j] + at_offset + 1);
2454 end_offset = at_offset + strlen(urls[i].text);
2455
2456 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2457 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2458
2459 /* This assumes no templates start with the URL itself, and that they
2460 have at least two characters before the URL text */
2461 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2462 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2463 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2464 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2465 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2466 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2467
2468 if (urls[i].is_url)
2469 {
2470 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2471 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2472 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2473 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2474 }
2475 else
2476 {
2477 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2478 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2479 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2480 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2481 }
2482 if (buffer[end_offset] != '\0')
2483 {
2484 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2485 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2486 if (buffer[end_offset +1] != '\0')
2487 {
2488 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2489 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2490 }
2491 }
2492 }
2493
2494 /* Set selection with X to the URL */
2495 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2496 char * at_pos;
2497 int at_offset;
2498 int end_offset;
2499
2500 at_pos = strchr(templates_delim[j], 'X');
2501 at_offset = at_pos - templates_delim[j];
2502 end_offset = at_offset + strlen(urls[i].text);
2503
2504 st.codepage = CP_ACP;
2505 st.flags = ST_DEFAULT;
2506 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2507 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2508 st.flags = ST_SELECTION;
2509 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2510 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2511 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2512
2513 /* This assumes no templates start with the URL itself, and that they
2514 have at least two characters before the URL text */
2515 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2516 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2517 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2518 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2519 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2520 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2521
2522 if (urls[i].is_url)
2523 {
2524 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2525 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2526 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2527 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2528 }
2529 else
2530 {
2531 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2532 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2533 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2534 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2535 }
2536 if (buffer[end_offset] != '\0')
2537 {
2538 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2539 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2540 if (buffer[end_offset +1] != '\0')
2541 {
2542 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2543 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2544 }
2545 }
2546 }
2547
2548 /* Set selection with X to the first character of the URL, then the rest */
2549 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2550 char * at_pos;
2551 int at_offset;
2552 int end_offset;
2553
2554 at_pos = strchr(templates_delim[j], 'X');
2555 at_offset = at_pos - templates_delim[j];
2556 end_offset = at_offset + strlen(urls[i].text);
2557
2558 strcpy(buffer, "YY");
2559 buffer[0] = urls[i].text[0];
2560
2561 st.codepage = CP_ACP;
2562 st.flags = ST_DEFAULT;
2563 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2564 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2565 st.flags = ST_SELECTION;
2566 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2567 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2568 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2569 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2570 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2571
2572 /* This assumes no templates start with the URL itself, and that they
2573 have at least two characters before the URL text */
2574 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2576 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2578 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2579 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2580
2581 if (urls[i].is_url)
2582 {
2583 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2584 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2585 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2586 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2587 }
2588 else
2589 {
2590 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2591 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2592 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2593 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2594 }
2595 if (buffer[end_offset] != '\0')
2596 {
2597 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2598 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2599 if (buffer[end_offset +1] != '\0')
2600 {
2601 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2602 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2603 }
2604 }
2605 }
2606
2607 DestroyWindow(hwndRichEdit);
2608 hwndRichEdit = NULL;
2609 }
2610
2611 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2612 /* Test just the first two URL examples for brevity */
2613 for (i = 0; i < 2; i++) {
2614 hwndRichEdit = new_richedit(parent);
2615
2616 /* Set selection with X to the URL */
2617 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2618 char * at_pos;
2619 int at_offset;
2620 int end_offset;
2621
2622 at_pos = strchr(templates_delim[j], 'X');
2623 at_offset = at_pos - templates_delim[j];
2624 end_offset = at_offset + strlen(urls[i].text);
2625
2626 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2627 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2628 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2629 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2630 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2631
2632 /* This assumes no templates start with the URL itself, and that they
2633 have at least two characters before the URL text */
2634 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2635 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2636 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2637 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2638 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2640
2641 if (urls[i].is_url)
2642 {
2643 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2644 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2645 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2646 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2647 }
2648 else
2649 {
2650 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2651 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2652 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2653 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2654 }
2655 if (buffer[end_offset] != '\0')
2656 {
2657 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2658 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2659 if (buffer[end_offset +1] != '\0')
2660 {
2661 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2662 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2663 }
2664 }
2665 }
2666
2667 /* Set selection with X to the first character of the URL, then the rest */
2668 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2669 char * at_pos;
2670 int at_offset;
2671 int end_offset;
2672
2673 at_pos = strchr(templates_delim[j], 'X');
2674 at_offset = at_pos - templates_delim[j];
2675 end_offset = at_offset + strlen(urls[i].text);
2676
2677 strcpy(buffer, "YY");
2678 buffer[0] = urls[i].text[0];
2679
2680 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2681 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2682 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2683 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2684 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2685 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2686 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2687
2688 /* This assumes no templates start with the URL itself, and that they
2689 have at least two characters before the URL text */
2690 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2692 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2693 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2694 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2696
2697 if (urls[i].is_url)
2698 {
2699 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2700 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2701 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2702 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2703 }
2704 else
2705 {
2706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2710 }
2711 if (buffer[end_offset] != '\0')
2712 {
2713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2715 if (buffer[end_offset +1] != '\0')
2716 {
2717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2719 }
2720 }
2721 }
2722
2723 DestroyWindow(hwndRichEdit);
2724 hwndRichEdit = NULL;
2725 }
2726
2728}
2729
2730static void test_EM_SCROLL(void)
2731{
2732 int i, j;
2733 int r; /* return value */
2734 int expr; /* expected return value */
2735 HWND hwndRichEdit = new_richedit(NULL);
2736 int y_before, y_after; /* units of lines of text */
2737
2738 /* test a richedit box containing a single line of text */
2739 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2740 expr = 0x00010000;
2741 for (i = 0; i < 4; i++) {
2742 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2743
2744 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2745 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2746 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2747 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2748 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2749 "(i == %d)\n", y_after, i);
2750 }
2751
2752 /*
2753 * test a richedit box that will scroll. There are two general
2754 * cases: the case without any long lines and the case with a long
2755 * line.
2756 */
2757 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2758 if (i == 0)
2759 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2760 else
2761 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2762 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2763 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2764 "LONG LINE \nb\nc\nd\ne");
2765 for (j = 0; j < 12; j++) /* reset scroll position to top */
2766 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2767
2768 /* get first visible line */
2769 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2770 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2771
2772 /* get new current first visible line */
2773 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2774
2775 ok(((r & 0xffffff00) == 0x00010000) &&
2776 ((r & 0x000000ff) != 0x00000000),
2777 "EM_SCROLL page down didn't scroll by a small positive number of "
2778 "lines (r == 0x%08x)\n", r);
2779 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2780 "(line %d scrolled to line %d\n", y_before, y_after);
2781
2782 y_before = y_after;
2783
2784 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2785 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2786 ok(((r & 0xffffff00) == 0x0001ff00),
2787 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2788 "(r == 0x%08x)\n", r);
2789 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2790 "%d scrolled to line %d\n", y_before, y_after);
2791
2792 y_before = y_after;
2793
2794 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2795
2796 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2797
2798 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2799 "(r == 0x%08x)\n", r);
2800 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2801 "1 line (%d scrolled to %d)\n", y_before, y_after);
2802
2803 y_before = y_after;
2804
2805 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2806
2807 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2808
2809 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2810 "(r == 0x%08x)\n", r);
2811 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2812 "line (%d scrolled to %d)\n", y_before, y_after);
2813
2814 y_before = y_after;
2815
2816 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2817 SB_LINEUP, 0); /* lineup beyond top */
2818
2819 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2820
2821 ok(r == 0x00010000,
2822 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2823 ok(y_before == y_after,
2824 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2825
2826 y_before = y_after;
2827
2828 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2829 SB_PAGEUP, 0);/*page up beyond top */
2830
2831 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2832
2833 ok(r == 0x00010000,
2834 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2835 ok(y_before == y_after,
2836 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2837
2838 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2839 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2840 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2841 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2842 SB_PAGEDOWN, 0); /* page down beyond bot */
2843 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2844
2845 ok(r == 0x00010000,
2846 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2847 ok(y_before == y_after,
2848 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2849 y_before, y_after);
2850
2851 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2852 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2853 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2854
2855 ok(r == 0x00010000,
2856 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2857 ok(y_before == y_after,
2858 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2859 y_before, y_after);
2860 }
2861 DestroyWindow(hwndRichEdit);
2862}
2863
2864static unsigned int recursionLevel = 0;
2865static unsigned int WM_SIZE_recursionLevel = 0;
2868
2870{
2871 LRESULT r;
2872
2873 if (bailedOutOfRecursion) return 0;
2874 if (recursionLevel >= 32) {
2876 return 0;
2877 }
2878
2880 switch (message) {
2881 case WM_SIZE:
2883 r = richeditProc(hwnd, message, wParam, lParam);
2884 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2887 break;
2888 default:
2889 r = richeditProc(hwnd, message, wParam, lParam);
2890 break;
2891 }
2893 return r;
2894}
2895
2897{
2898 HWND hwndRichEdit;
2899 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2900 SCROLLINFO si;
2901 WNDCLASSA cls;
2902 BOOL r;
2903
2904 /* These tests show that richedit should temporarily refrain from automatically
2905 hiding or showing its scrollbars (vertical at least) when an explicit request
2906 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2907 Some applications depend on forced showing (when otherwise richedit would
2908 hide the vertical scrollbar) and are thrown on an endless recursive loop
2909 if richedit auto-hides the scrollbar again. Apparently they never heard of
2910 the ES_DISABLENOSCROLL style... */
2911
2912 hwndRichEdit = new_richedit(NULL);
2913
2914 /* Test default scrollbar visibility behavior */
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2920 "Vertical scrollbar is visible, should be invisible.\n");
2921 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923 si.nPage, si.nMin, si.nMax);
2924
2925 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2926 memset(&si, 0, sizeof(si));
2927 si.cbSize = sizeof(si);
2928 si.fMask = SIF_PAGE | SIF_RANGE;
2929 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931 "Vertical scrollbar is visible, should be invisible.\n");
2932 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934 si.nPage, si.nMin, si.nMax);
2935
2936 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2937 memset(&si, 0, sizeof(si));
2938 si.cbSize = sizeof(si);
2939 si.fMask = SIF_PAGE | SIF_RANGE;
2940 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2942 "Vertical scrollbar is invisible, should be visible.\n");
2943 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2944 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2945 si.nPage, si.nMin, si.nMax);
2946
2947 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2948 even though it hides the scrollbar */
2949 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950 memset(&si, 0, sizeof(si));
2951 si.cbSize = sizeof(si);
2952 si.fMask = SIF_PAGE | SIF_RANGE;
2953 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955 "Vertical scrollbar is visible, should be invisible.\n");
2956 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958 si.nPage, si.nMin, si.nMax);
2959
2960 /* Setting non-scrolling text again does *not* reset scrollbar range */
2961 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2962 memset(&si, 0, sizeof(si));
2963 si.cbSize = sizeof(si);
2964 si.fMask = SIF_PAGE | SIF_RANGE;
2965 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2966 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2967 "Vertical scrollbar is visible, should be invisible.\n");
2968 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2969 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2970 si.nPage, si.nMin, si.nMax);
2971
2972 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2973 memset(&si, 0, sizeof(si));
2974 si.cbSize = sizeof(si);
2975 si.fMask = SIF_PAGE | SIF_RANGE;
2976 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2977 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2978 "Vertical scrollbar is visible, should be invisible.\n");
2979 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2980 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2981 si.nPage, si.nMin, si.nMax);
2982
2983 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2984 memset(&si, 0, sizeof(si));
2985 si.cbSize = sizeof(si);
2986 si.fMask = SIF_PAGE | SIF_RANGE;
2987 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2988 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2989 "Vertical scrollbar is visible, should be invisible.\n");
2990 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2991 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2992 si.nPage, si.nMin, si.nMax);
2993
2994 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2995 memset(&si, 0, sizeof(si));
2996 si.cbSize = sizeof(si);
2997 si.fMask = SIF_PAGE | SIF_RANGE;
2998 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2999 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3000 "Vertical scrollbar is visible, should be invisible.\n");
3001 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3002 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3003 si.nPage, si.nMin, si.nMax);
3004
3005 DestroyWindow(hwndRichEdit);
3006
3007 /* Test again, with ES_DISABLENOSCROLL style */
3009
3010 /* Test default scrollbar visibility behavior */
3011 memset(&si, 0, sizeof(si));
3012 si.cbSize = sizeof(si);
3013 si.fMask = SIF_PAGE | SIF_RANGE;
3014 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016 "Vertical scrollbar is invisible, should be visible.\n");
3017 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3018 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3019 si.nPage, si.nMin, si.nMax);
3020
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 == 1,
3029 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3030 si.nPage, si.nMin, si.nMax);
3031
3032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3033 memset(&si, 0, sizeof(si));
3034 si.cbSize = sizeof(si);
3035 si.fMask = SIF_PAGE | SIF_RANGE;
3036 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3038 "Vertical scrollbar is invisible, should be visible.\n");
3039 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3040 "reported page/range is %d (%d..%d)\n",
3041 si.nPage, si.nMin, si.nMax);
3042
3043 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3044 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3045 memset(&si, 0, sizeof(si));
3046 si.cbSize = sizeof(si);
3047 si.fMask = SIF_PAGE | SIF_RANGE;
3048 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050 "Vertical scrollbar is invisible, should be visible.\n");
3051 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3052 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3053 si.nPage, si.nMin, si.nMax);
3054
3055 /* Setting non-scrolling text again does *not* reset scrollbar range */
3056 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3057 memset(&si, 0, sizeof(si));
3058 si.cbSize = sizeof(si);
3059 si.fMask = SIF_PAGE | SIF_RANGE;
3060 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3061 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3062 "Vertical scrollbar is invisible, should be visible.\n");
3063 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3064 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3065 si.nPage, si.nMin, si.nMax);
3066
3067 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3068 memset(&si, 0, sizeof(si));
3069 si.cbSize = sizeof(si);
3070 si.fMask = SIF_PAGE | SIF_RANGE;
3071 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3072 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3073 "Vertical scrollbar is invisible, should be visible.\n");
3074 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3075 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3076 si.nPage, si.nMin, si.nMax);
3077
3078 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084 "Vertical scrollbar is invisible, should be visible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3086 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3087 si.nPage, si.nMin, si.nMax);
3088
3089 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3090 memset(&si, 0, sizeof(si));
3091 si.cbSize = sizeof(si);
3092 si.fMask = SIF_PAGE | SIF_RANGE;
3093 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3094 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3095 "Vertical scrollbar is invisible, should be visible.\n");
3096 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3097 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3098 si.nPage, si.nMin, si.nMax);
3099
3100 DestroyWindow(hwndRichEdit);
3101
3102 /* Test behavior with explicit visibility request, using ShowScrollBar() */
3103 hwndRichEdit = new_richedit(NULL);
3104
3105 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3106 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3107 memset(&si, 0, sizeof(si));
3108 si.cbSize = sizeof(si);
3109 si.fMask = SIF_PAGE | SIF_RANGE;
3110 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3112 "Vertical scrollbar is invisible, should be visible.\n");
3113 todo_wine {
3114 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3115 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3116 si.nPage, si.nMin, si.nMax);
3117 }
3118
3119 /* Ditto, see above */
3120 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3121 memset(&si, 0, sizeof(si));
3122 si.cbSize = sizeof(si);
3123 si.fMask = SIF_PAGE | SIF_RANGE;
3124 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126 "Vertical scrollbar is invisible, should be visible.\n");
3127 todo_wine {
3128 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3129 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3130 si.nPage, si.nMin, si.nMax);
3131 }
3132
3133 /* Ditto, see above */
3134 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3135 memset(&si, 0, sizeof(si));
3136 si.cbSize = sizeof(si);
3137 si.fMask = SIF_PAGE | SIF_RANGE;
3138 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3139 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3140 "Vertical scrollbar is invisible, should be visible.\n");
3141 todo_wine {
3142 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3143 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3144 si.nPage, si.nMin, si.nMax);
3145 }
3146
3147 /* Ditto, see above */
3148 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3149 memset(&si, 0, sizeof(si));
3150 si.cbSize = sizeof(si);
3151 si.fMask = SIF_PAGE | SIF_RANGE;
3152 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3153 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3154 "Vertical scrollbar is invisible, should be visible.\n");
3155 todo_wine {
3156 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3157 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3158 si.nPage, si.nMin, si.nMax);
3159 }
3160
3161 /* Ditto, see above */
3162 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3163 memset(&si, 0, sizeof(si));
3164 si.cbSize = sizeof(si);
3165 si.fMask = SIF_PAGE | SIF_RANGE;
3166 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3168 "Vertical scrollbar is invisible, should be visible.\n");
3169 todo_wine {
3170 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3171 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3172 si.nPage, si.nMin, si.nMax);
3173 }
3174
3175 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3176 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3177 memset(&si, 0, sizeof(si));
3178 si.cbSize = sizeof(si);
3179 si.fMask = SIF_PAGE | SIF_RANGE;
3180 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3181 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3182 "Vertical scrollbar is visible, should be invisible.\n");
3183 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3184 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3185 si.nPage, si.nMin, si.nMax);
3186
3187 DestroyWindow(hwndRichEdit);
3188
3189 hwndRichEdit = new_richedit(NULL);
3190
3191 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3192 memset(&si, 0, sizeof(si));
3193 si.cbSize = sizeof(si);
3194 si.fMask = SIF_PAGE | SIF_RANGE;
3195 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3196 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3197 "Vertical scrollbar is visible, should be invisible.\n");
3198 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3199 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3200 si.nPage, si.nMin, si.nMax);
3201
3202 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3203 memset(&si, 0, sizeof(si));
3204 si.cbSize = sizeof(si);
3205 si.fMask = SIF_PAGE | SIF_RANGE;
3206 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3207 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3208 "Vertical scrollbar is visible, should be invisible.\n");
3209 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3210 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3211 si.nPage, si.nMin, si.nMax);
3212
3213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3214 memset(&si, 0, sizeof(si));
3215 si.cbSize = sizeof(si);
3216 si.fMask = SIF_PAGE | SIF_RANGE;
3217 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3218 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3219 "Vertical scrollbar is visible, should be invisible.\n");
3220 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3221 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3222 si.nPage, si.nMin, si.nMax);
3223
3224 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3225 memset(&si, 0, sizeof(si));
3226 si.cbSize = sizeof(si);
3227 si.fMask = SIF_PAGE | SIF_RANGE;
3228 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3229 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3230 "Vertical scrollbar is visible, should be invisible.\n");
3231 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3232 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3233 si.nPage, si.nMin, si.nMax);
3234
3235 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3236 memset(&si, 0, sizeof(si));
3237 si.cbSize = sizeof(si);
3238 si.fMask = SIF_PAGE | SIF_RANGE;
3239 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3240 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3241 "Vertical scrollbar is invisible, should be visible.\n");
3242 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3243 "reported page/range is %d (%d..%d)\n",
3244 si.nPage, si.nMin, si.nMax);
3245
3246 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3247 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3248 memset(&si, 0, sizeof(si));
3249 si.cbSize = sizeof(si);
3250 si.fMask = SIF_PAGE | SIF_RANGE;
3251 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3252 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3253 "Vertical scrollbar is visible, should be invisible.\n");
3254 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3255 "reported page/range is %d (%d..%d)\n",
3256 si.nPage, si.nMin, si.nMax);
3257
3258 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3259 memset(&si, 0, sizeof(si));
3260 si.cbSize = sizeof(si);
3261 si.fMask = SIF_PAGE | SIF_RANGE;
3262 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3263 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3264 "Vertical scrollbar is visible, should be invisible.\n");
3265 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3266 "reported page/range is %d (%d..%d)\n",
3267 si.nPage, si.nMin, si.nMax);
3268
3269 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3270 EM_SCROLL will make visible any forcefully invisible scrollbar */
3271 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3272 memset(&si, 0, sizeof(si));
3273 si.cbSize = sizeof(si);
3274 si.fMask = SIF_PAGE | SIF_RANGE;
3275 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3276 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3277 "Vertical scrollbar is invisible, should be visible.\n");
3278 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3279 "reported page/range is %d (%d..%d)\n",
3280 si.nPage, si.nMin, si.nMax);
3281
3282 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3283 memset(&si, 0, sizeof(si));
3284 si.cbSize = sizeof(si);
3285 si.fMask = SIF_PAGE | SIF_RANGE;
3286 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3287 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3288 "Vertical scrollbar is visible, should be invisible.\n");
3289 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3290 "reported page/range is %d (%d..%d)\n",
3291 si.nPage, si.nMin, si.nMax);
3292
3293 /* Again, EM_SCROLL, with SB_LINEUP */
3294 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3295 memset(&si, 0, sizeof(si));
3296 si.cbSize = sizeof(si);
3297 si.fMask = SIF_PAGE | SIF_RANGE;
3298 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3299 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3300 "Vertical scrollbar is invisible, should be visible.\n");
3301 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3302 "reported page/range is %d (%d..%d)\n",
3303 si.nPage, si.nMin, si.nMax);
3304
3305 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3306 memset(&si, 0, sizeof(si));
3307 si.cbSize = sizeof(si);
3308 si.fMask = SIF_PAGE | SIF_RANGE;
3309 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3310 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3311 "Vertical scrollbar is visible, should be invisible.\n");
3312 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3313 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3314 si.nPage, si.nMin, si.nMax);
3315
3316 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3317 memset(&si, 0, sizeof(si));
3318 si.cbSize = sizeof(si);
3319 si.fMask = SIF_PAGE | SIF_RANGE;
3320 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3321 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3322 "Vertical scrollbar is invisible, should be visible.\n");
3323 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3324 "reported page/range is %d (%d..%d)\n",
3325 si.nPage, si.nMin, si.nMax);
3326
3327 DestroyWindow(hwndRichEdit);
3328
3329
3330 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3331 hwndRichEdit = new_richedit(NULL);
3332
3333#define ENABLE_WS_VSCROLL(hwnd) \
3334 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3335#define DISABLE_WS_VSCROLL(hwnd) \
3336 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3337
3338 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3339 ENABLE_WS_VSCROLL(hwndRichEdit);
3340 memset(&si, 0, sizeof(si));
3341 si.cbSize = sizeof(si);
3342 si.fMask = SIF_PAGE | SIF_RANGE;
3343 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3344 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3345 "Vertical scrollbar is invisible, should be visible.\n");
3346 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3347 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3348 si.nPage, si.nMin, si.nMax);
3349
3350 /* Ditto, see above */
3351 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3352 memset(&si, 0, sizeof(si));
3353 si.cbSize = sizeof(si);
3354 si.fMask = SIF_PAGE | SIF_RANGE;
3355 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3356 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3357 "Vertical scrollbar is invisible, should be visible.\n");
3358 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3359 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3360 si.nPage, si.nMin, si.nMax);
3361
3362 /* Ditto, see above */
3363 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3364 memset(&si, 0, sizeof(si));
3365 si.cbSize = sizeof(si);
3366 si.fMask = SIF_PAGE | SIF_RANGE;
3367 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3368 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3369 "Vertical scrollbar is invisible, should be visible.\n");
3370 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3371 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3372 si.nPage, si.nMin, si.nMax);
3373
3374 /* Ditto, see above */
3375 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3376 memset(&si, 0, sizeof(si));
3377 si.cbSize = sizeof(si);
3378 si.fMask = SIF_PAGE | SIF_RANGE;
3379 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3380 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3381 "Vertical scrollbar is invisible, should be visible.\n");
3382 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3383 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3384 si.nPage, si.nMin, si.nMax);
3385
3386 /* Ditto, see above */
3387 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3388 memset(&si, 0, sizeof(si));
3389 si.cbSize = sizeof(si);
3390 si.fMask = SIF_PAGE | SIF_RANGE;
3391 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3392 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3393 "Vertical scrollbar is invisible, should be visible.\n");
3394 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3395 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3396 si.nPage, si.nMin, si.nMax);
3397
3398 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3399 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3400 memset(&si, 0, sizeof(si));
3401 si.cbSize = sizeof(si);
3402 si.fMask = SIF_PAGE | SIF_RANGE;
3403 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3404 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3405 "Vertical scrollbar is visible, should be invisible.\n");
3406 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3407 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3408 si.nPage, si.nMin, si.nMax);
3409
3410 DestroyWindow(hwndRichEdit);
3411
3412 hwndRichEdit = new_richedit(NULL);
3413
3414 DISABLE_WS_VSCROLL(hwndRichEdit);
3415 memset(&si, 0, sizeof(si));
3416 si.cbSize = sizeof(si);
3417 si.fMask = SIF_PAGE | SIF_RANGE;
3418 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3419 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3420 "Vertical scrollbar is visible, should be invisible.\n");
3421 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3422 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3423 si.nPage, si.