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 /* Try a negative position. */
653 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
654 ok(pt.x == 1, "pt.x = %d\n", pt.x);
655
656 DestroyWindow(hwndRichEdit);
657 }
658
659 static void test_EM_SETCHARFORMAT(void)
660 {
661 HWND hwndRichEdit = new_richedit(NULL);
662 CHARFORMAT2 cf2;
663 int rc = 0;
664 int tested_effects[] = {
665 CFE_BOLD,
666 CFE_ITALIC,
667 CFE_UNDERLINE,
668 CFE_STRIKEOUT,
669 CFE_PROTECTED,
670 CFE_LINK,
671 CFE_SUBSCRIPT,
672 CFE_SUPERSCRIPT,
673 0
674 };
675 int i;
676 CHARRANGE cr;
677
678 /* Invalid flags, CHARFORMAT2 structure blanked out */
679 memset(&cf2, 0, sizeof(cf2));
680 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
681 (LPARAM) &cf2);
682 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
683
684 /* A valid flag, CHARFORMAT2 structure blanked out */
685 memset(&cf2, 0, sizeof(cf2));
686 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
687 (LPARAM) &cf2);
688 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
689
690 /* A valid flag, CHARFORMAT2 structure blanked out */
691 memset(&cf2, 0, sizeof(cf2));
692 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
693 (LPARAM) &cf2);
694 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
695
696 /* A valid flag, CHARFORMAT2 structure blanked out */
697 memset(&cf2, 0, sizeof(cf2));
698 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
699 (LPARAM) &cf2);
700 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
701
702 /* A valid flag, CHARFORMAT2 structure blanked out */
703 memset(&cf2, 0, sizeof(cf2));
704 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
705 (LPARAM) &cf2);
706 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
707
708 /* Invalid flags, CHARFORMAT2 structure minimally filled */
709 memset(&cf2, 0, sizeof(cf2));
710 cf2.cbSize = sizeof(CHARFORMAT2);
711 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
712 (LPARAM) &cf2);
713 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
714 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
715 ok(rc == FALSE, "Should not be able to undo here.\n");
716 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
717
718 /* A valid flag, CHARFORMAT2 structure minimally filled */
719 memset(&cf2, 0, sizeof(cf2));
720 cf2.cbSize = sizeof(CHARFORMAT2);
721 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
722 (LPARAM) &cf2);
723 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
724 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
725 ok(rc == FALSE, "Should not be able to undo here.\n");
726 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
727
728 /* A valid flag, CHARFORMAT2 structure minimally filled */
729 memset(&cf2, 0, sizeof(cf2));
730 cf2.cbSize = sizeof(CHARFORMAT2);
731 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
732 (LPARAM) &cf2);
733 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
734 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
735 ok(rc == FALSE, "Should not be able to undo here.\n");
736 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
737
738 /* A valid flag, CHARFORMAT2 structure minimally filled */
739 memset(&cf2, 0, sizeof(cf2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
742 (LPARAM) &cf2);
743 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
744 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
745 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
746 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
747
748 /* A valid flag, CHARFORMAT2 structure minimally filled */
749 memset(&cf2, 0, sizeof(cf2));
750 cf2.cbSize = sizeof(CHARFORMAT2);
751 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
752 (LPARAM) &cf2);
753 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
754 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
755 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
756 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
757
758 cf2.cbSize = sizeof(CHARFORMAT2);
759 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
760 (LPARAM) &cf2);
761
762 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
763 cf2.cbSize = sizeof(CHARFORMAT2);
764 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
765 (LPARAM) &cf2);
766 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
767 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
768
769 /* wParam==0 is default char format, does not set modify */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
777
778 /* wParam==SCF_SELECTION sets modify if nonempty selection */
779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
780 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
781 ok(rc == 0, "Text marked as modified, expected not modified!\n");
782 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
783 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
784 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
785 ok(rc == 0, "Text marked as modified, expected not modified!\n");
786
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == 0, "Text marked as modified, expected not modified!\n");
790 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
792 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
793 ok(rc == 0, "Text marked as modified, expected not modified!\n");
794 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
799
800 /* wParam==SCF_ALL sets modify regardless of whether text is present */
801 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
802 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803 ok(rc == 0, "Text marked as modified, expected not modified!\n");
804 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
807 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
808
809 DestroyWindow(hwndRichEdit);
810
811 /* EM_GETCHARFORMAT tests */
812 for (i = 0; tested_effects[i]; i++)
813 {
814 hwndRichEdit = new_richedit(NULL);
815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
816
817 /* Need to set a TrueType font to get consistent CFM_BOLD results */
818 memset(&cf2, 0, sizeof(CHARFORMAT2));
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
821 cf2.dwEffects = 0;
822 strcpy(cf2.szFaceName, "Courier New");
823 cf2.wWeight = FW_DONTCARE;
824 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
825
826 memset(&cf2, 0, sizeof(CHARFORMAT2));
827 cf2.cbSize = sizeof(CHARFORMAT2);
828 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
829 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
830 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
831 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
832 ||
833 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
834 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
835 ok((cf2.dwEffects & tested_effects[i]) == 0,
836 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
837
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 cf2.dwMask = tested_effects[i];
841 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
842 cf2.dwMask = CFM_SUPERSCRIPT;
843 cf2.dwEffects = tested_effects[i];
844 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
845 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
846
847 memset(&cf2, 0, sizeof(CHARFORMAT2));
848 cf2.cbSize = sizeof(CHARFORMAT2);
849 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
850 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
851 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
852 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
853 ||
854 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
855 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
856 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
857 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
858
859 memset(&cf2, 0, sizeof(CHARFORMAT2));
860 cf2.cbSize = sizeof(CHARFORMAT2);
861 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
862 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
863 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
864 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
865 ||
866 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
867 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
868 ok((cf2.dwEffects & tested_effects[i]) == 0,
869 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
870
871 memset(&cf2, 0, sizeof(CHARFORMAT2));
872 cf2.cbSize = sizeof(CHARFORMAT2);
873 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
874 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
875 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
876 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
877 ||
878 (cf2.dwMask & tested_effects[i]) == 0),
879 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
880
881 DestroyWindow(hwndRichEdit);
882 }
883
884 for (i = 0; tested_effects[i]; i++)
885 {
886 hwndRichEdit = new_richedit(NULL);
887 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
888
889 /* Need to set a TrueType font to get consistent CFM_BOLD results */
890 memset(&cf2, 0, sizeof(CHARFORMAT2));
891 cf2.cbSize = sizeof(CHARFORMAT2);
892 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
893 cf2.dwEffects = 0;
894 strcpy(cf2.szFaceName, "Courier New");
895 cf2.wWeight = FW_DONTCARE;
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
897
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 cf2.dwMask = tested_effects[i];
901 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
902 cf2.dwMask = CFM_SUPERSCRIPT;
903 cf2.dwEffects = tested_effects[i];
904 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
905 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
906
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
910 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
911 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
912 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
913 ||
914 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
915 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
916 ok((cf2.dwEffects & tested_effects[i]) == 0,
917 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
918
919 memset(&cf2, 0, sizeof(CHARFORMAT2));
920 cf2.cbSize = sizeof(CHARFORMAT2);
921 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
922 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
923 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
924 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
925 ||
926 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
927 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
928 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
929 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
930
931 memset(&cf2, 0, sizeof(CHARFORMAT2));
932 cf2.cbSize = sizeof(CHARFORMAT2);
933 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
934 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
935 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
936 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
937 ||
938 (cf2.dwMask & tested_effects[i]) == 0),
939 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
940 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
941 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
942
943 DestroyWindow(hwndRichEdit);
944 }
945
946 /* Effects applied on an empty selection should take effect when selection is
947 replaced with text */
948 hwndRichEdit = new_richedit(NULL);
949 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
950 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
951
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 cf2.dwMask = CFM_BOLD;
955 cf2.dwEffects = CFE_BOLD;
956 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957
958 /* Selection is now nonempty */
959 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
960
961 memset(&cf2, 0, sizeof(CHARFORMAT2));
962 cf2.cbSize = sizeof(CHARFORMAT2);
963 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
964 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
965
966 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
967 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
968 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
969 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
970
971
972 /* Set two effects on an empty selection */
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
974 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
975
976 memset(&cf2, 0, sizeof(CHARFORMAT2));
977 cf2.cbSize = sizeof(CHARFORMAT2);
978 cf2.dwMask = CFM_BOLD;
979 cf2.dwEffects = CFE_BOLD;
980 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
981 cf2.dwMask = CFM_ITALIC;
982 cf2.dwEffects = CFE_ITALIC;
983 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984
985 /* Selection is now nonempty */
986 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
987
988 memset(&cf2, 0, sizeof(CHARFORMAT2));
989 cf2.cbSize = sizeof(CHARFORMAT2);
990 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
991 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
992
993 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
994 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
995 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
996 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
997
998 /* Setting the (empty) selection to exactly the same place as before should
999 NOT clear the insertion style! */
1000 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1002
1003 memset(&cf2, 0, sizeof(CHARFORMAT2));
1004 cf2.cbSize = sizeof(CHARFORMAT2);
1005 cf2.dwMask = CFM_BOLD;
1006 cf2.dwEffects = CFE_BOLD;
1007 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1008
1009 /* Empty selection in same place, insert style should NOT be forgotten here. */
1010 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1011
1012 /* Selection is now nonempty */
1013 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1014
1015 memset(&cf2, 0, sizeof(CHARFORMAT2));
1016 cf2.cbSize = sizeof(CHARFORMAT2);
1017 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1018 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1019
1020 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1021 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1022 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1023 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1024
1025 /* Ditto with EM_EXSETSEL */
1026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1027 cr.cpMin = 2; cr.cpMax = 2;
1028 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1029
1030 memset(&cf2, 0, sizeof(CHARFORMAT2));
1031 cf2.cbSize = sizeof(CHARFORMAT2);
1032 cf2.dwMask = CFM_BOLD;
1033 cf2.dwEffects = CFE_BOLD;
1034 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1035
1036 /* Empty selection in same place, insert style should NOT be forgotten here. */
1037 cr.cpMin = 2; cr.cpMax = 2;
1038 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1039
1040 /* Selection is now nonempty */
1041 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1042
1043 memset(&cf2, 0, sizeof(CHARFORMAT2));
1044 cf2.cbSize = sizeof(CHARFORMAT2);
1045 cr.cpMin = 2; cr.cpMax = 6;
1046 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1047 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1048
1049 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1050 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1051 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1052 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1053
1054 DestroyWindow(hwndRichEdit);
1055 }
1056
1057 static void test_EM_SETTEXTMODE(void)
1058 {
1059 HWND hwndRichEdit = new_richedit(NULL);
1060 CHARFORMAT2 cf2, cf2test;
1061 CHARRANGE cr;
1062 int rc = 0;
1063
1064 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1065 /*Insert text into the control*/
1066
1067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1068
1069 /*Attempt to change the control to plain text mode*/
1070 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1071 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1072
1073 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1074 If rich text is pasted, it should have the same formatting as the rest
1075 of the text in the control*/
1076
1077 /*Italicize the text
1078 *NOTE: If the default text was already italicized, the test will simply
1079 reverse; in other words, it will copy a regular "wine" into a plain
1080 text window that uses an italicized format*/
1081 cf2.cbSize = sizeof(CHARFORMAT2);
1082 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1083 (LPARAM) &cf2);
1084
1085 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1086 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1087
1088 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1089 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1090
1091 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1092 however, SCF_ALL has been implemented*/
1093 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1094 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1095
1096 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1097 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1098
1099 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1100
1101 /*Select the string "wine"*/
1102 cr.cpMin = 0;
1103 cr.cpMax = 4;
1104 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1105
1106 /*Copy the italicized "wine" to the clipboard*/
1107 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1108
1109 /*Reset the formatting to default*/
1110 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1111 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1112 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1113
1114 /*Clear the text in the control*/
1115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1116
1117 /*Switch to Plain Text Mode*/
1118 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1119 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1120
1121 /*Input "wine" again in normal format*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1123
1124 /*Paste the italicized "wine" into the control*/
1125 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1126
1127 /*Select a character from the first "wine" string*/
1128 cr.cpMin = 2;
1129 cr.cpMax = 3;
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1131
1132 /*Retrieve its formatting*/
1133 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1134 (LPARAM) &cf2);
1135
1136 /*Select a character from the second "wine" string*/
1137 cr.cpMin = 5;
1138 cr.cpMax = 6;
1139 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1140
1141 /*Retrieve its formatting*/
1142 cf2test.cbSize = sizeof(CHARFORMAT2);
1143 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1144 (LPARAM) &cf2test);
1145
1146 /*Compare the two formattings*/
1147 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1148 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1149 cf2.dwEffects, cf2test.dwEffects);
1150 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1151 printing "wine" in the current format(normal)
1152 pasting "wine" from the clipboard(italicized)
1153 comparing the two formats(should differ)*/
1154
1155 /*Attempt to switch with text in control*/
1156 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1157 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1158
1159 /*Clear control*/
1160 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1161
1162 /*Switch into Rich Text mode*/
1163 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1164 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1165
1166 /*Print "wine" in normal formatting into the control*/
1167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1168
1169 /*Paste italicized "wine" into the control*/
1170 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1171
1172 /*Select text from the first "wine" string*/
1173 cr.cpMin = 1;
1174 cr.cpMax = 3;
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1176
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1179 (LPARAM) &cf2);
1180
1181 /*Select text from the second "wine" string*/
1182 cr.cpMin = 6;
1183 cr.cpMax = 7;
1184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1185
1186 /*Retrieve its formatting*/
1187 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1188 (LPARAM) &cf2test);
1189
1190 /*Test that the two formattings are not the same*/
1191 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1192 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1193 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1194
1195 DestroyWindow(hwndRichEdit);
1196 }
1197
1198 static void test_SETPARAFORMAT(void)
1199 {
1200 HWND hwndRichEdit = new_richedit(NULL);
1201 PARAFORMAT2 fmt;
1202 HRESULT ret;
1203 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1204 fmt.cbSize = sizeof(PARAFORMAT2);
1205 fmt.dwMask = PFM_ALIGNMENT;
1206 fmt.wAlignment = PFA_LEFT;
1207
1208 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1209 ok(ret != 0, "expected non-zero got %d\n", ret);
1210
1211 fmt.cbSize = sizeof(PARAFORMAT2);
1212 fmt.dwMask = -1;
1213 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1214 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1215 * between richedit different native builds of riched20.dll
1216 * used on different Windows versions. */
1217 ret &= ~PFM_TABLEROWDELIMITER;
1218 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1219
1220 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1221 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1222
1223 DestroyWindow(hwndRichEdit);
1224 }
1225
1226 static void test_TM_PLAINTEXT(void)
1227 {
1228 /*Tests plain text properties*/
1229
1230 HWND hwndRichEdit = new_richedit(NULL);
1231 CHARFORMAT2 cf2, cf2test;
1232 CHARRANGE cr;
1233 int rc = 0;
1234
1235 /*Switch to plain text mode*/
1236
1237 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1238 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1239
1240 /*Fill control with text*/
1241
1242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1243
1244 /*Select some text and bold it*/
1245
1246 cr.cpMin = 10;
1247 cr.cpMax = 20;
1248 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1249 cf2.cbSize = sizeof(CHARFORMAT2);
1250 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1251 (LPARAM) &cf2);
1252
1253 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1254 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1255
1256 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1257 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1258
1259 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1260 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1261
1262 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1263 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1264
1265 /*Get the formatting of those characters*/
1266
1267 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1268
1269 /*Get the formatting of some other characters*/
1270 cf2test.cbSize = sizeof(CHARFORMAT2);
1271 cr.cpMin = 21;
1272 cr.cpMax = 30;
1273 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1274 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1275
1276 /*Test that they are the same as plain text allows only one formatting*/
1277
1278 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1279 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1280 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1281
1282 /*Fill the control with a "wine" string, which when inserted will be bold*/
1283
1284 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1285
1286 /*Copy the bolded "wine" string*/
1287
1288 cr.cpMin = 0;
1289 cr.cpMax = 4;
1290 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1291 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1292
1293 /*Swap back to rich text*/
1294
1295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1296 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1297
1298 /*Set the default formatting to bold italics*/
1299
1300 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1301 cf2.dwMask |= CFM_ITALIC;
1302 cf2.dwEffects ^= CFE_ITALIC;
1303 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1304 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1305
1306 /*Set the text in the control to "wine", which will be bold and italicized*/
1307
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1309
1310 /*Paste the plain text "wine" string, which should take the insert
1311 formatting, which at the moment is bold italics*/
1312
1313 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1314
1315 /*Select the first "wine" string and retrieve its formatting*/
1316
1317 cr.cpMin = 1;
1318 cr.cpMax = 3;
1319 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1320 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1321
1322 /*Select the second "wine" string and retrieve its formatting*/
1323
1324 cr.cpMin = 5;
1325 cr.cpMax = 7;
1326 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1327 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1328
1329 /*Compare the two formattings. They should be the same.*/
1330
1331 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1332 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1333 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1334 DestroyWindow(hwndRichEdit);
1335 }
1336
1337 static void test_WM_GETTEXT(void)
1338 {
1339 HWND hwndRichEdit = new_richedit(NULL);
1340 static const char text[] = "Hello. My name is RichEdit!";
1341 static const char text2[] = "Hello. My name is RichEdit!\r";
1342 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1343 char buffer[1024] = {0};
1344 int result;
1345
1346 /* Baseline test with normal-sized buffer */
1347 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1348 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1349 ok(result == lstrlen(buffer),
1350 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1351 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1352 result = strcmp(buffer,text);
1353 ok(result == 0,
1354 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1355
1356 /* Test for returned value of WM_GETTEXTLENGTH */
1357 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1358 ok(result == lstrlen(text),
1359 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1360 result, lstrlen(text));
1361
1362 /* Test for behavior in overflow case */
1363 memset(buffer, 0, 1024);
1364 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1365 ok(result == 0 ||
1366 result == lstrlenA(text) - 1, /* XP, win2k3 */
1367 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1368 result = strcmp(buffer,text);
1369 if (result)
1370 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1371 ok(result == 0,
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1373
1374 /* Baseline test with normal-sized buffer and carriage return */
1375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1376 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1377 ok(result == lstrlen(buffer),
1378 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1379 result = strcmp(buffer,text2_after);
1380 ok(result == 0,
1381 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1382
1383 /* Test for returned value of WM_GETTEXTLENGTH */
1384 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1385 ok(result == lstrlen(text2_after),
1386 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1387 result, lstrlen(text2_after));
1388
1389 /* Test for behavior of CRLF conversion in case of overflow */
1390 memset(buffer, 0, 1024);
1391 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1392 ok(result == 0 ||
1393 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1394 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1395 result = strcmp(buffer,text2);
1396 if (result)
1397 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1398 ok(result == 0,
1399 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1400
1401 DestroyWindow(hwndRichEdit);
1402 }
1403
1404 static void test_EM_GETTEXTRANGE(void)
1405 {
1406 HWND hwndRichEdit = new_richedit(NULL);
1407 const char * text1 = "foo bar\r\nfoo bar";
1408 const char * text2 = "foo bar\rfoo bar";
1409 const char * expect = "bar\rfoo";
1410 char buffer[1024] = {0};
1411 LRESULT result;
1412 TEXTRANGEA textRange;
1413
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1415
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1422
1423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1424
1425 textRange.lpstrText = buffer;
1426 textRange.chrg.cpMin = 4;
1427 textRange.chrg.cpMax = 11;
1428 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1429 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1430 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1431
1432 DestroyWindow(hwndRichEdit);
1433 }
1434
1435 static void test_EM_GETSELTEXT(void)
1436 {
1437 HWND hwndRichEdit = new_richedit(NULL);
1438 const char * text1 = "foo bar\r\nfoo bar";
1439 const char * text2 = "foo bar\rfoo bar";
1440 const char * expect = "bar\rfoo";
1441 char buffer[1024] = {0};
1442 LRESULT result;
1443
1444 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1445
1446 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1447 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1448 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1449 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1450
1451 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1452
1453 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1454 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1455 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1456 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1457
1458 DestroyWindow(hwndRichEdit);
1459 }
1460
1461 /* FIXME: need to test unimplemented options and robustly test wparam */
1462 static void test_EM_SETOPTIONS(void)
1463 {
1464 HWND hwndRichEdit = new_richedit(NULL);
1465 static const char text[] = "Hello. My name is RichEdit!";
1466 char buffer[1024] = {0};
1467
1468 /* NEGATIVE TESTING - NO OPTIONS SET */
1469 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1470 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1471
1472 /* testing no readonly by sending 'a' to the control*/
1473 SetFocus(hwndRichEdit);
1474 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1475 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1476 ok(buffer[0]=='a',
1477 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1479
1480 /* READONLY - sending 'a' to the control */
1481 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1482 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1483 SetFocus(hwndRichEdit);
1484 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1485 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1486 ok(buffer[0]==text[0],
1487 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1488
1489 DestroyWindow(hwndRichEdit);
1490 }
1491
1492 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1493 {
1494 CHARFORMAT2W text_format;
1495 text_format.cbSize = sizeof(text_format);
1496 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1497 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1498 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1499 }
1500
1501 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1502 {
1503 int link_present = 0;
1504
1505 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1506 if (is_url)
1507 { /* control text is url; should get CFE_LINK */
1508 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1509 }
1510 else
1511 {
1512 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1513 }
1514 }
1515
1516 static HWND new_static_wnd(HWND parent) {
1517 return new_window("Static", 0, parent);
1518 }
1519
1520 static void test_EM_AUTOURLDETECT(void)
1521 {
1522 /* DO NOT change the properties of the first two elements. To shorten the
1523 tests, all tests after WM_SETTEXT test just the first two elements -
1524 one non-URL and one URL */
1525 struct urls_s {
1526 const char *text;
1527 int is_url;
1528 } urls[12] = {
1529 {"winehq.org", 0},
1530 {"http://www.winehq.org", 1},
1531 {"http//winehq.org", 0},
1532 {"ww.winehq.org", 0},
1533 {"www.winehq.org", 1},
1534 {"ftp://192.168.1.1", 1},
1535 {"ftp//192.168.1.1", 0},
1536 {"mailto:your@email.com", 1},
1537 {"prospero:prosperoserver", 1},
1538 {"telnet:test", 1},
1539 {"news:newserver", 1},
1540 {"wais:waisserver", 1}
1541 };
1542
1543 int i, j;
1544 int urlRet=-1;
1545 HWND hwndRichEdit, parent;
1546
1547 /* All of the following should cause the URL to be detected */
1548 const char * templates_delim[] = {
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\r on it",
1552 "This is some text with ---X--- on it",
1553 "This is some text with \"X\" on it",
1554 "This is some text with 'X' on it",
1555 "This is some text with 'X' on it",
1556 "This is some text with :X: on it",
1557
1558 "This text ends with X",
1559
1560 "This is some text with X) on it",
1561 "This is some text with X--- on it",
1562 "This is some text with X\" on it",
1563 "This is some text with X' on it",
1564 "This is some text with X: on it",
1565
1566 "This is some text with (X on it",
1567 "This is some text with \rX on it",
1568 "This is some text with ---X on it",
1569 "This is some text with \"X on it",
1570 "This is some text with 'X on it",
1571 "This is some text with :X on it",
1572 };
1573 /* None of these should cause the URL to be detected */
1574 const char * templates_non_delim[] = {
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 "This is some text with %X on it",
1588 "This is some text with #X on it",
1589 "This is some text with @X on it",
1590 "This is some text with \\X on it",
1591 };
1592 /* All of these cause the URL detection to be extended by one more byte,
1593 thus demonstrating that the tested character is considered as part
1594 of the URL. */
1595 const char * templates_xten_delim[] = {
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 "This is some text with X% on it",
1601 "This is some text with X# on it",
1602 "This is some text with X@ on it",
1603 "This is some text with X\\ on it",
1604 };
1605 char buffer[1024];
1606
1607 parent = new_static_wnd(NULL);
1608 hwndRichEdit = new_richedit(parent);
1609 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1610 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1611 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1612 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1613 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1614 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1615 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1616 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1617 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1618 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1619 /* for each url, check the text to see if CFE_LINK effect is present */
1620 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1621
1622 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1624 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1625
1626 /* Link detection should happen immediately upon WM_SETTEXT */
1627 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1629 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1630 }
1631 DestroyWindow(hwndRichEdit);
1632
1633 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1634 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1635 hwndRichEdit = new_richedit(parent);
1636
1637 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1638 char * at_pos;
1639 int at_offset;
1640 int end_offset;
1641
1642 at_pos = strchr(templates_delim[j], 'X');
1643 at_offset = at_pos - templates_delim[j];
1644 strncpy(buffer, templates_delim[j], at_offset);
1645 buffer[at_offset] = '\0';
1646 strcat(buffer, urls[i].text);
1647 strcat(buffer, templates_delim[j] + at_offset + 1);
1648 end_offset = at_offset + strlen(urls[i].text);
1649
1650 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1651 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1652
1653 /* This assumes no templates start with the URL itself, and that they
1654 have at least two characters before the URL text */
1655 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1656 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1657 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1658 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1659 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1660 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1661
1662 if (urls[i].is_url)
1663 {
1664 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1665 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1666 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1667 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1668 }
1669 else
1670 {
1671 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1672 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1673 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1674 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1675 }
1676 if (buffer[end_offset] != '\0')
1677 {
1678 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1679 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1680 if (buffer[end_offset +1] != '\0')
1681 {
1682 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1683 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1684 }
1685 }
1686 }
1687
1688 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1689 char * at_pos;
1690 int at_offset;
1691 int end_offset;
1692
1693 at_pos = strchr(templates_non_delim[j], 'X');
1694 at_offset = at_pos - templates_non_delim[j];
1695 strncpy(buffer, templates_non_delim[j], at_offset);
1696 buffer[at_offset] = '\0';
1697 strcat(buffer, urls[i].text);
1698 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1699 end_offset = at_offset + strlen(urls[i].text);
1700
1701 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1702 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1703
1704 /* This assumes no templates start with the URL itself, and that they
1705 have at least two characters before the URL text */
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1710 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1711 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1712
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1715 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1716 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1717 if (buffer[end_offset] != '\0')
1718 {
1719 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1720 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1721 if (buffer[end_offset +1] != '\0')
1722 {
1723 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1724 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1725 }
1726 }
1727 }
1728
1729 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1730 char * at_pos;
1731 int at_offset;
1732 int end_offset;
1733
1734 at_pos = strchr(templates_xten_delim[j], 'X');
1735 at_offset = at_pos - templates_xten_delim[j];
1736 strncpy(buffer, templates_xten_delim[j], at_offset);
1737 buffer[at_offset] = '\0';
1738 strcat(buffer, urls[i].text);
1739 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1740 end_offset = at_offset + strlen(urls[i].text);
1741
1742 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1744
1745 /* This assumes no templates start with the URL itself, and that they
1746 have at least two characters before the URL text */
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1749 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1750 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1751 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1752 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1753
1754 if (urls[i].is_url)
1755 {
1756 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1757 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1758 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1759 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1760 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1761 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1762 }
1763 else
1764 {
1765 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1766 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1767 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1768 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1769 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1770 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1771 }
1772 if (buffer[end_offset +1] != '\0')
1773 {
1774 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1775 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1776 if (buffer[end_offset +2] != '\0')
1777 {
1778 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1779 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1780 }
1781 }
1782 }
1783
1784 DestroyWindow(hwndRichEdit);
1785 hwndRichEdit = NULL;
1786 }
1787
1788 /* Test detection of URLs within normal text - WM_CHAR case. */
1789 /* Test only the first two URL examples for brevity */
1790 for (i = 0; i < 2; i++) {
1791 hwndRichEdit = new_richedit(parent);
1792
1793 /* Also for brevity, test only the first three delimiters */
1794 for (j = 0; j < 3; j++) {
1795 char * at_pos;
1796 int at_offset;
1797 int end_offset;
1798 int u, v;
1799
1800 at_pos = strchr(templates_delim[j], 'X');
1801 at_offset = at_pos - templates_delim[j];
1802 end_offset = at_offset + strlen(urls[i].text);
1803
1804 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1805 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1806 for (u = 0; templates_delim[j][u]; u++) {
1807 if (templates_delim[j][u] == '\r') {
1808 simulate_typing_characters(hwndRichEdit, "\r");
1809 } else if (templates_delim[j][u] != 'X') {
1810 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1811 } else {
1812 for (v = 0; urls[i].text[v]; v++) {
1813 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1814 }
1815 }
1816 }
1817 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1818
1819 /* This assumes no templates start with the URL itself, and that they
1820 have at least two characters before the URL text */
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1823 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1824 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1827
1828 if (urls[i].is_url)
1829 {
1830 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1831 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1832 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1833 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1834 }
1835 else
1836 {
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1841 }
1842 if (buffer[end_offset] != '\0')
1843 {
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1846 if (buffer[end_offset +1] != '\0')
1847 {
1848 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1849 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1850 }
1851 }
1852
1853 /* The following will insert a paragraph break after the first character
1854 of the URL candidate, thus breaking the URL. It is expected that the
1855 CFE_LINK attribute should break across both pieces of the URL */
1856 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1857 simulate_typing_characters(hwndRichEdit, "\r");
1858 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1859
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1864 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1865 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1866
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1869 /* end_offset moved because of paragraph break */
1870 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1871 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1872 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1873 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1874 {
1875 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1876 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1877 if (buffer[end_offset +2] != '\0')
1878 {
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1881 }
1882 }
1883
1884 /* The following will remove the just-inserted paragraph break, thus
1885 restoring the URL */
1886 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1887 simulate_typing_characters(hwndRichEdit, "\b");
1888 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1889
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1892 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1893 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1894 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1895 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1896
1897 if (urls[i].is_url)
1898 {
1899 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1900 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1901 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1902 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1903 }
1904 else
1905 {
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1908 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1909 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1910 }
1911 if (buffer[end_offset] != '\0')
1912 {
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1915 if (buffer[end_offset +1] != '\0')
1916 {
1917 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1918 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1919 }
1920 }
1921 }
1922 DestroyWindow(hwndRichEdit);
1923 hwndRichEdit = NULL;
1924 }
1925
1926 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1927 /* Test just the first two URL examples for brevity */
1928 for (i = 0; i < 2; i++) {
1929 SETTEXTEX st;
1930
1931 hwndRichEdit = new_richedit(parent);
1932
1933 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1934 be detected:
1935 1) Set entire text, a la WM_SETTEXT
1936 2) Set a selection of the text to the URL
1937 3) Set a portion of the text at a time, which eventually results in
1938 an URL
1939 All of them should give equivalent results
1940 */
1941
1942 /* Set entire text in one go, like WM_SETTEXT */
1943 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1944 char * at_pos;
1945 int at_offset;
1946 int end_offset;
1947
1948 st.codepage = CP_ACP;
1949 st.flags = ST_DEFAULT;
1950
1951 at_pos = strchr(templates_delim[j], 'X');
1952 at_offset = at_pos - templates_delim[j];
1953 strncpy(buffer, templates_delim[j], at_offset);
1954 buffer[at_offset] = '\0';
1955 strcat(buffer, urls[i].text);
1956 strcat(buffer, templates_delim[j] + at_offset + 1);
1957 end_offset = at_offset + strlen(urls[i].text);
1958
1959 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1960 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1961
1962 /* This assumes no templates start with the URL itself, and that they
1963 have at least two characters before the URL text */
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1966 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1967 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1970
1971 if (urls[i].is_url)
1972 {
1973 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1974 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1975 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1976 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1977 }
1978 else
1979 {
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1982 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1983 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1984 }
1985 if (buffer[end_offset] != '\0')
1986 {
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1989 if (buffer[end_offset +1] != '\0')
1990 {
1991 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1992 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1993 }
1994 }
1995 }
1996
1997 /* Set selection with X to the URL */
1998 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1999 char * at_pos;
2000 int at_offset;
2001 int end_offset;
2002
2003 at_pos = strchr(templates_delim[j], 'X');
2004 at_offset = at_pos - templates_delim[j];
2005 end_offset = at_offset + strlen(urls[i].text);
2006
2007 st.codepage = CP_ACP;
2008 st.flags = ST_DEFAULT;
2009 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2010 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2011 st.flags = ST_SELECTION;
2012 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2013 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2014 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2015
2016 /* This assumes no templates start with the URL itself, and that they
2017 have at least two characters before the URL text */
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2020 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2021 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2024
2025 if (urls[i].is_url)
2026 {
2027 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2028 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2029 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2030 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2031 }
2032 else
2033 {
2034 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2035 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2036 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2037 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2038 }
2039 if (buffer[end_offset] != '\0')
2040 {
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2043 if (buffer[end_offset +1] != '\0')
2044 {
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2047 }
2048 }
2049 }
2050
2051 /* Set selection with X to the first character of the URL, then the rest */
2052 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2053 char * at_pos;
2054 int at_offset;
2055 int end_offset;
2056
2057 at_pos = strchr(templates_delim[j], 'X');
2058 at_offset = at_pos - templates_delim[j];
2059 end_offset = at_offset + strlen(urls[i].text);
2060
2061 strcpy(buffer, "YY");
2062 buffer[0] = urls[i].text[0];
2063
2064 st.codepage = CP_ACP;
2065 st.flags = ST_DEFAULT;
2066 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2067 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2068 st.flags = ST_SELECTION;
2069 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2070 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2071 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2072 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2073 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2074
2075 /* This assumes no templates start with the URL itself, and that they
2076 have at least two characters before the URL text */
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2081 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2082 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2083
2084 if (urls[i].is_url)
2085 {
2086 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2087 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2088 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2089 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2090 }
2091 else
2092 {
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2097 }
2098 if (buffer[end_offset] != '\0')
2099 {
2100 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2101 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2102 if (buffer[end_offset +1] != '\0')
2103 {
2104 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2105 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2106 }
2107 }
2108 }
2109
2110 DestroyWindow(hwndRichEdit);
2111 hwndRichEdit = NULL;
2112 }
2113
2114 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2115 /* Test just the first two URL examples for brevity */
2116 for (i = 0; i < 2; i++) {
2117 hwndRichEdit = new_richedit(parent);
2118
2119 /* Set selection with X to the URL */
2120 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2121 char * at_pos;
2122 int at_offset;
2123 int end_offset;
2124
2125 at_pos = strchr(templates_delim[j], 'X');
2126 at_offset = at_pos - templates_delim[j];
2127 end_offset = at_offset + strlen(urls[i].text);
2128
2129 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2130 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2131 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2132 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2133 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2134
2135 /* This assumes no templates start with the URL itself, and that they
2136 have at least two characters before the URL text */
2137 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2138 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2141 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2142 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2143
2144 if (urls[i].is_url)
2145 {
2146 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2147 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2148 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2149 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2150 }
2151 else
2152 {
2153 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2154 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2155 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2156 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2157 }
2158 if (buffer[end_offset] != '\0')
2159 {
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2162 if (buffer[end_offset +1] != '\0')
2163 {
2164 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2165 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2166 }
2167 }
2168 }
2169
2170 /* Set selection with X to the first character of the URL, then the rest */
2171 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2172 char * at_pos;
2173 int at_offset;
2174 int end_offset;
2175
2176 at_pos = strchr(templates_delim[j], 'X');
2177 at_offset = at_pos - templates_delim[j];
2178 end_offset = at_offset + strlen(urls[i].text);
2179
2180 strcpy(buffer, "YY");
2181 buffer[0] = urls[i].text[0];
2182
2183 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2184 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2185 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2186 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2187 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2188 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2189 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2190
2191 /* This assumes no templates start with the URL itself, and that they
2192 have at least two characters before the URL text */
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2197 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2198 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2199
2200 if (urls[i].is_url)
2201 {
2202 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2203 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2204 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2205 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2206 }
2207 else
2208 {
2209 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2210 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2213 }
2214 if (buffer[end_offset] != '\0')
2215 {
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2218 if (buffer[end_offset +1] != '\0')
2219 {
2220 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2221 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2222 }
2223 }
2224 }
2225
2226 DestroyWindow(hwndRichEdit);
2227 hwndRichEdit = NULL;
2228 }
2229
2230 DestroyWindow(parent);
2231 }
2232
2233 static void test_EM_SCROLL(void)
2234 {
2235 int i, j;
2236 int r; /* return value */
2237 int expr; /* expected return value */
2238 HWND hwndRichEdit = new_richedit(NULL);
2239 int y_before, y_after; /* units of lines of text */
2240
2241 /* test a richedit box containing a single line of text */
2242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2243 expr = 0x00010000;
2244 for (i = 0; i < 4; i++) {
2245 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2246
2247 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2248 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2249 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2250 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2251 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2252 "(i == %d)\n", y_after, i);
2253 }
2254
2255 /*
2256 * test a richedit box that will scroll. There are two general
2257 * cases: the case without any long lines and the case with a long
2258 * line.
2259 */
2260 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2261 if (i == 0)
2262 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2263 else
2264 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2265 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2266 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2267 "LONG LINE \nb\nc\nd\ne");
2268 for (j = 0; j < 12; j++) /* reset scroll position to top */
2269 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2270
2271 /* get first visible line */
2272 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2273 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2274
2275 /* get new current first visible line */
2276 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2277
2278 ok(((r & 0xffffff00) == 0x00010000) &&
2279 ((r & 0x000000ff) != 0x00000000),
2280 "EM_SCROLL page down didn't scroll by a small positive number of "
2281 "lines (r == 0x%08x)\n", r);
2282 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2283 "(line %d scrolled to line %d\n", y_before, y_after);
2284
2285 y_before = y_after;
2286
2287 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2288 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2289 ok(((r & 0xffffff00) == 0x0001ff00),
2290 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2291 "(r == 0x%08x)\n", r);
2292 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2293 "%d scrolled to line %d\n", y_before, y_after);
2294
2295 y_before = y_after;
2296
2297 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2298
2299 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2300
2301 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2302 "(r == 0x%08x)\n", r);
2303 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2304 "1 line (%d scrolled to %d)\n", y_before, y_after);
2305
2306 y_before = y_after;
2307
2308 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2309
2310 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2311
2312 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2313 "(r == 0x%08x)\n", r);
2314 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2315 "line (%d scrolled to %d)\n", y_before, y_after);
2316
2317 y_before = y_after;
2318
2319 r = SendMessage(hwndRichEdit, EM_SCROLL,
2320 SB_LINEUP, 0); /* lineup beyond top */
2321
2322 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2323
2324 ok(r == 0x00010000,
2325 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2326 ok(y_before == y_after,
2327 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2328
2329 y_before = y_after;
2330
2331 r = SendMessage(hwndRichEdit, EM_SCROLL,
2332 SB_PAGEUP, 0);/*page up beyond top */
2333
2334 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2335
2336 ok(r == 0x00010000,
2337 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2338 ok(y_before == y_after,
2339 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2340
2341 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2342 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2343 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2344 r = SendMessage(hwndRichEdit, EM_SCROLL,
2345 SB_PAGEDOWN, 0); /* page down beyond bot */
2346 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2347
2348 ok(r == 0x00010000,
2349 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2350 ok(y_before == y_after,
2351 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2352 y_before, y_after);
2353
2354 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2355 SendMessage(hwndRichEdit, EM_SCROLL,
2356 SB_LINEDOWN, 0); /* line down beyond bot */
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2358
2359 ok(r == 0x00010000,
2360 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2361 ok(y_before == y_after,
2362 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2363 y_before, y_after);
2364 }
2365 DestroyWindow(hwndRichEdit);
2366 }
2367
2368 unsigned int recursionLevel = 0;
2369 unsigned int WM_SIZE_recursionLevel = 0;
2370 BOOL bailedOutOfRecursion = FALSE;
2371 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2372
2373 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2374 {
2375 LRESULT r;
2376
2377 if (bailedOutOfRecursion) return 0;
2378 if (recursionLevel >= 32) {
2379 bailedOutOfRecursion = TRUE;
2380 return 0;
2381 }
2382
2383 recursionLevel++;
2384 switch (message) {
2385 case WM_SIZE:
2386 WM_SIZE_recursionLevel++;
2387 r = richeditProc(hwnd, message, wParam, lParam);
2388 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2389 ShowScrollBar(hwnd, SB_VERT, TRUE);
2390 WM_SIZE_recursionLevel--;
2391 break;
2392 default:
2393 r = richeditProc(hwnd, message, wParam, lParam);
2394 break;
2395 }
2396 recursionLevel--;
2397 return r;
2398 }
2399
2400 static void test_scrollbar_visibility(void)
2401 {
2402 HWND hwndRichEdit;
2403 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2404 SCROLLINFO si;
2405 WNDCLASSA cls;
2406 BOOL r;
2407
2408 /* These tests show that richedit should temporarily refrain from automatically
2409 hiding or showing its scrollbars (vertical at least) when an explicit request
2410 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2411 Some applications depend on forced showing (when otherwise richedit would
2412 hide the vertical scrollbar) and are thrown on an endless recursive loop
2413 if richedit auto-hides the scrollbar again. Apparently they never heard of
2414 the ES_DISABLENOSCROLL style... */
2415
2416 hwndRichEdit = new_richedit(NULL);
2417
2418 /* Test default scrollbar visibility behavior */
2419 memset(&si, 0, sizeof(si));
2420 si.cbSize = sizeof(si);
2421 si.fMask = SIF_PAGE | SIF_RANGE;
2422 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2423 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2424 "Vertical scrollbar is visible, should be invisible.\n");
2425 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2426 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2427 si.nPage, si.nMin, si.nMax);
2428
2429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2430 memset(&si, 0, sizeof(si));
2431 si.cbSize = sizeof(si);
2432 si.fMask = SIF_PAGE | SIF_RANGE;
2433 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2434 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2435 "Vertical scrollbar is visible, should be invisible.\n");
2436 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2437 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2438 si.nPage, si.nMin, si.nMax);
2439
2440 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2441 memset(&si, 0, sizeof(si));
2442 si.cbSize = sizeof(si);
2443 si.fMask = SIF_PAGE | SIF_RANGE;
2444 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2445 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2446 "Vertical scrollbar is invisible, should be visible.\n");
2447 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2448 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2449 si.nPage, si.nMin, si.nMax);
2450
2451 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2452 even though it hides the scrollbar */
2453 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2461 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2462 si.nPage, si.nMin, si.nMax);
2463
2464 /* Setting non-scrolling text again does *not* reset scrollbar range */
2465 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2466 memset(&si, 0, sizeof(si));
2467 si.cbSize = sizeof(si);
2468 si.fMask = SIF_PAGE | SIF_RANGE;
2469 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2470 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2471 "Vertical scrollbar is visible, should be invisible.\n");
2472 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2473 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2474 si.nPage, si.nMin, si.nMax);
2475
2476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2477 memset(&si, 0, sizeof(si));
2478 si.cbSize = sizeof(si);
2479 si.fMask = SIF_PAGE | SIF_RANGE;
2480 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2481 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2482 "Vertical scrollbar is visible, should be invisible.\n");
2483 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2484 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2485 si.nPage, si.nMin, si.nMax);
2486
2487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2488 memset(&si, 0, sizeof(si));
2489 si.cbSize = sizeof(si);
2490 si.fMask = SIF_PAGE | SIF_RANGE;
2491 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2492 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2493 "Vertical scrollbar is visible, should be invisible.\n");
2494 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2495 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2496 si.nPage, si.nMin, si.nMax);
2497
2498 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2499 memset(&si, 0, sizeof(si));
2500 si.cbSize = sizeof(si);
2501 si.fMask = SIF_PAGE | SIF_RANGE;
2502 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2503 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2504 "Vertical scrollbar is visible, should be invisible.\n");
2505 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2506 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2507 si.nPage, si.nMin, si.nMax);
2508
2509 DestroyWindow(hwndRichEdit);
2510
2511 /* Test again, with ES_DISABLENOSCROLL style */
2512 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2513
2514 /* Test default scrollbar visibility behavior */
2515 memset(&si, 0, sizeof(si));
2516 si.cbSize = sizeof(si);
2517 si.fMask = SIF_PAGE | SIF_RANGE;
2518 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2519 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2520 "Vertical scrollbar is invisible, should be visible.\n");
2521 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2522 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2523 si.nPage, si.nMin, si.nMax);
2524
2525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2526 memset(&si, 0, sizeof(si));
2527 si.cbSize = sizeof(si);
2528 si.fMask = SIF_PAGE | SIF_RANGE;
2529 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2530 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2531 "Vertical scrollbar is invisible, should be visible.\n");
2532 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2533 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2534 si.nPage, si.nMin, si.nMax);
2535
2536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2537 memset(&si, 0, sizeof(si));
2538 si.cbSize = sizeof(si);
2539 si.fMask = SIF_PAGE | SIF_RANGE;
2540 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2541 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2542 "Vertical scrollbar is invisible, should be visible.\n");
2543 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2544 "reported page/range is %d (%d..%d)\n",
2545 si.nPage, si.nMin, si.nMax);
2546
2547 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2549 memset(&si, 0, sizeof(si));
2550 si.cbSize = sizeof(si);
2551 si.fMask = SIF_PAGE | SIF_RANGE;
2552 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2553 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2554 "Vertical scrollbar is invisible, should be visible.\n");
2555 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2556 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2557 si.nPage, si.nMin, si.nMax);
2558
2559 /* Setting non-scrolling text again does *not* reset scrollbar range */
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2568 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2569 si.nPage, si.nMin, si.nMax);
2570
2571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2579 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2580 si.nPage, si.nMin, si.nMax);
2581
2582 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2583 memset(&si, 0, sizeof(si));
2584 si.cbSize = sizeof(si);
2585 si.fMask = SIF_PAGE | SIF_RANGE;
2586 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2587 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2588 "Vertical scrollbar is invisible, should be visible.\n");
2589 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2590 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2591 si.nPage, si.nMin, si.nMax);
2592
2593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2594 memset(&si, 0, sizeof(si));
2595 si.cbSize = sizeof(si);
2596 si.fMask = SIF_PAGE | SIF_RANGE;
2597 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2598 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2599 "Vertical scrollbar is invisible, should be visible.\n");
2600 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2601 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2602 si.nPage, si.nMin, si.nMax);
2603
2604 DestroyWindow(hwndRichEdit);
2605
2606 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2607 hwndRichEdit = new_richedit(NULL);
2608
2609 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2610 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2611 memset(&si, 0, sizeof(si));
2612 si.cbSize = sizeof(si);
2613 si.fMask = SIF_PAGE | SIF_RANGE;
2614 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2615 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2616 "Vertical scrollbar is invisible, should be visible.\n");
2617 todo_wine {
2618 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2619 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2620 si.nPage, si.nMin, si.nMax);
2621 }
2622
2623 /* Ditto, see above */
2624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2625 memset(&si, 0, sizeof(si));
2626 si.cbSize = sizeof(si);
2627 si.fMask = SIF_PAGE | SIF_RANGE;
2628 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2629 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2630 "Vertical scrollbar is invisible, should be visible.\n");
2631 todo_wine {
2632 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2633 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2634 si.nPage, si.nMin, si.nMax);
2635 }
2636
2637 /* Ditto, see above */
2638 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2639 memset(&si, 0, sizeof(si));
2640 si.cbSize = sizeof(si);
2641 si.fMask = SIF_PAGE | SIF_RANGE;
2642 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2643 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2644 "Vertical scrollbar is invisible, should be visible.\n");
2645 todo_wine {
2646 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2647 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2648 si.nPage, si.nMin, si.nMax);
2649 }
2650
2651 /* Ditto, see above */
2652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2653 memset(&si, 0, sizeof(si));
2654 si.cbSize = sizeof(si);
2655 si.fMask = SIF_PAGE | SIF_RANGE;
2656 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2657 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2658 "Vertical scrollbar is invisible, should be visible.\n");
2659 todo_wine {
2660 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2661 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2662 si.nPage, si.nMin, si.nMax);
2663 }
2664
2665 /* Ditto, see above */
2666 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2667 memset(&si, 0, sizeof(si));
2668 si.cbSize = sizeof(si);
2669 si.fMask = SIF_PAGE | SIF_RANGE;
2670 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2671 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2672 "Vertical scrollbar is invisible, should be visible.\n");
2673 todo_wine {
2674 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2675 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2676 si.nPage, si.nMin, si.nMax);
2677 }
2678
2679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2681 memset(&si, 0, sizeof(si));
2682 si.cbSize = sizeof(si);
2683 si.fMask = SIF_PAGE | SIF_RANGE;
2684 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2686 "Vertical scrollbar is visible, should be invisible.\n");
2687 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2688 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2689 si.nPage, si.nMin, si.nMax);
2690
2691 DestroyWindow(hwndRichEdit);
2692
2693 hwndRichEdit = new_richedit(NULL);
2694
2695 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2696 memset(&si, 0, sizeof(si));
2697 si.cbSize = sizeof(si);
2698 si.fMask = SIF_PAGE | SIF_RANGE;
2699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2700 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2701 "Vertical scrollbar is visible, should be invisible.\n");
2702 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2703 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2704 si.nPage, si.nMin, si.nMax);
2705
2706 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2707 memset(&si, 0, sizeof(si));
2708 si.cbSize = sizeof(si);
2709 si.fMask = SIF_PAGE | SIF_RANGE;
2710 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2711 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2712 "Vertical scrollbar is visible, should be invisible.\n");
2713 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2714 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2715 si.nPage, si.nMin, si.nMax);
2716
2717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2718 memset(&si, 0, sizeof(si));
2719 si.cbSize = sizeof(si);
2720 si.fMask = SIF_PAGE | SIF_RANGE;
2721 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2722 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2723 "Vertical scrollbar is visible, should be invisible.\n");
2724 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2725 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2726 si.nPage, si.nMin, si.nMax);
2727
2728 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2729 memset(&si, 0, sizeof(si));
2730 si.cbSize = sizeof(si);
2731 si.fMask = SIF_PAGE | SIF_RANGE;
2732 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2733 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2734 "Vertical scrollbar is visible, should be invisible.\n");
2735 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2736 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2737 si.nPage, si.nMin, si.nMax);
2738
2739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2740 memset(&si, 0, sizeof(si));
2741 si.cbSize = sizeof(si);
2742 si.fMask = SIF_PAGE | SIF_RANGE;
2743 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2744 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2745 "Vertical scrollbar is invisible, should be visible.\n");
2746 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2747 "reported page/range is %d (%d..%d)\n",
2748 si.nPage, si.nMin, si.nMax);
2749
2750 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2751 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2752 memset(&si, 0, sizeof(si));
2753 si.cbSize = sizeof(si);
2754 si.fMask = SIF_PAGE | SIF_RANGE;
2755 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2756 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2757 "Vertical scrollbar is visible, should be invisible.\n");
2758 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2759 "reported page/range is %d (%d..%d)\n",
2760 si.nPage, si.nMin, si.nMax);
2761
2762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2763 memset(&si, 0, sizeof(si));
2764 si.cbSize = sizeof(si);
2765 si.fMask = SIF_PAGE | SIF_RANGE;
2766 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2767 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2768 "Vertical scrollbar is visible, should be invisible.\n");
2769 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2770 "reported page/range is %d (%d..%d)\n",
2771 si.nPage, si.nMin, si.nMax);
2772
2773 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2774 EM_SCROLL will make visible any forcefully invisible scrollbar */
2775 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2776 memset(&si, 0, sizeof(si));
2777 si.cbSize = sizeof(si);
2778 si.fMask = SIF_PAGE | SIF_RANGE;
2779 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2780 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2781 "Vertical scrollbar is invisible, should be visible.\n");
2782 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2783 "reported page/range is %d (%d..%d)\n",
2784 si.nPage, si.nMin, si.nMax);
2785
2786 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2787 memset(&si, 0, sizeof(si));
2788 si.cbSize = sizeof(si);
2789 si.fMask = SIF_PAGE | SIF_RANGE;
2790 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2791 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2792 "Vertical scrollbar is visible, should be invisible.\n");
2793 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2794 "reported page/range is %d (%d..%d)\n",
2795 si.nPage, si.nMin, si.nMax);
2796
2797 /* Again, EM_SCROLL, with SB_LINEUP */
2798 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2799 memset(&si, 0, sizeof(si));
2800 si.cbSize = sizeof(si);
2801 si.fMask = SIF_PAGE | SIF_RANGE;
2802 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2803 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2804 "Vertical scrollbar is invisible, should be visible.\n");
2805 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2806 "reported page/range is %d (%d..%d)\n",
2807 si.nPage, si.nMin, si.nMax);
2808
2809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2810 memset(&si, 0, sizeof(si));
2811 si.cbSize = sizeof(si);
2812 si.fMask = SIF_PAGE | SIF_RANGE;
2813 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2814 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2815 "Vertical scrollbar is visible, should be invisible.\n");
2816 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2817 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2818 si.nPage, si.nMin, si.nMax);
2819
2820 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2821 memset(&si, 0, sizeof(si));
2822 si.cbSize = sizeof(si);
2823 si.fMask = SIF_PAGE | SIF_RANGE;
2824 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2825 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2826 "Vertical scrollbar is invisible, should be visible.\n");
2827 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2828 "reported page/range is %d (%d..%d)\n",
2829 si.nPage, si.nMin, si.nMax);
2830
2831 DestroyWindow(hwndRichEdit);
2832
2833
2834 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2835 hwndRichEdit = new_richedit(NULL);
2836
2837 #define ENABLE_WS_VSCROLL(hwnd) \
2838 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2839 #define DISABLE_WS_VSCROLL(hwnd) \
2840 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2841
2842 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2843 ENABLE_WS_VSCROLL(hwndRichEdit);
2844 memset(&si, 0, sizeof(si));
2845 si.cbSize = sizeof(si);
2846 si.fMask = SIF_PAGE | SIF_RANGE;
2847 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2848 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2849 "Vertical scrollbar is invisible, should be visible.\n");
2850 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2851 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2852 si.nPage, si.nMin, si.nMax);
2853
2854 /* Ditto, see above */
2855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2856 memset(&si, 0, sizeof(si));
2857 si.cbSize = sizeof(si);
2858 si.fMask = SIF_PAGE | SIF_RANGE;
2859 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2860 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2861 "Vertical scrollbar is invisible, should be visible.\n");
2862 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2863 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2864 si.nPage, si.nMin, si.nMax);
2865
2866 /* Ditto, see above */
2867 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2868 memset(&si, 0, sizeof(si));
2869 si.cbSize = sizeof(si);
2870 si.fMask = SIF_PAGE | SIF_RANGE;
2871 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2872 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2873 "Vertical scrollbar is invisible, should be visible.\n");
2874 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2875 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2876 si.nPage, si.nMin, si.nMax);
2877
2878 /* Ditto, see above */
2879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2885 "Vertical scrollbar is invisible, should be visible.\n");
2886 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2887 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2888 si.nPage, si.nMin, si.nMax);
2889
2890 /* Ditto, see above */
2891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2892 memset(&si, 0, sizeof(si));
2893 si.cbSize = sizeof(si);
2894 si.fMask = SIF_PAGE | SIF_RANGE;
2895 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2896 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2897 "Vertical scrollbar is invisible, should be visible.\n");
2898 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2899 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2900 si.nPage, si.nMin, si.nMax);
2901
2902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2903 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2904 memset(&si, 0, sizeof(si));
2905 si.cbSize = sizeof(si);
2906 si.fMask = SIF_PAGE | SIF_RANGE;
2907 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2908 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2909 "Vertical scrollbar is visible, should be invisible.\n");
2910 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2911 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2912 si.nPage, si.nMin, si.nMax);
2913
2914 DestroyWindow(hwndRichEdit);
2915
2916 hwndRichEdit = new_richedit(NULL);
2917
2918 DISABLE_WS_VSCROLL(hwndRichEdit);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2924 "Vertical scrollbar is visible, should be invisible.\n");
2925 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2926 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2927 si.nPage, si.nMin, si.nMax);
2928
2929 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2930 memset(&si, 0, sizeof(si));
2931 si.cbSize = sizeof(si);
2932 si.fMask = SIF_PAGE | SIF_RANGE;
2933 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2934 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2935 "Vertical scrollbar is visible, should be invisible.\n");
2936 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2937 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2938 si.nPage, si.nMin, si.nMax);
2939
2940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2941 memset(&si, 0, sizeof(si));
2942 si.cbSize = sizeof(si);
2943 si.fMask = SIF_PAGE | SIF_RANGE;
2944 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2945 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2946 "Vertical scrollbar is visible, should be invisible.\n");
2947 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2948 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2949 si.nPage, si.nMin, si.nMax);
2950
2951 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2952 memset(&si, 0, sizeof(si));
2953 si.cbSize = sizeof(si);
2954 si.fMask = SIF_PAGE | SIF_RANGE;
2955 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2956 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2957 "Vertical scrollbar is visible, should be invisible.\n");
2958 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2959 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2960 si.nPage, si.nMin, si.nMax);
2961
2962 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2963 memset(&si, 0, sizeof(si));
2964 si.cbSize = sizeof(si);
2965 si.fMask = SIF_PAGE | SIF_RANGE;
2966 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2967 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2968 "Vertical scrollbar is invisible, should be visible.\n");
2969 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2970 "reported page/range is %d (%d..%d)\n",
2971 si.nPage, si.nMin, si.nMax);
2972
2973 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2974 DISABLE_WS_VSCROLL(hwndRichEdit);
2975 memset(&si, 0, sizeof(si));
2976 si.cbSize = sizeof(si);
2977 si.fMask = SIF_PAGE | SIF_RANGE;
2978 GetScrollInfo(hwndRichEdit,