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