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