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