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