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