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