aed6150fd953e4aa507fc37f01cc98d36f8f62cc
[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 int rc = 0;
751 int tested_effects[] = {
752 CFE_BOLD,
753 CFE_ITALIC,
754 CFE_UNDERLINE,
755 CFE_STRIKEOUT,
756 CFE_PROTECTED,
757 CFE_LINK,
758 CFE_SUBSCRIPT,
759 CFE_SUPERSCRIPT,
760 0
761 };
762 int i;
763 CHARRANGE cr;
764 LOCALESIGNATURE sig;
765 BOOL rtl;
766
767 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
768 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
769 (sig.lsUsb[3] & 0x08000000) != 0);
770
771 /* Invalid flags, CHARFORMAT2 structure blanked out */
772 memset(&cf2, 0, sizeof(cf2));
773 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
774 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
775
776 /* A valid flag, CHARFORMAT2 structure blanked out */
777 memset(&cf2, 0, sizeof(cf2));
778 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
779 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
780
781 /* A valid flag, CHARFORMAT2 structure blanked out */
782 memset(&cf2, 0, sizeof(cf2));
783 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
784 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
785
786 /* A valid flag, CHARFORMAT2 structure blanked out */
787 memset(&cf2, 0, sizeof(cf2));
788 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
789 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
790
791 /* A valid flag, CHARFORMAT2 structure blanked out */
792 memset(&cf2, 0, sizeof(cf2));
793 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
794 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
795
796 /* Invalid flags, CHARFORMAT2 structure minimally filled */
797 memset(&cf2, 0, sizeof(cf2));
798 cf2.cbSize = sizeof(CHARFORMAT2A);
799 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
800 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
801 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
802 ok(rc == FALSE, "Should not be able to undo here.\n");
803 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
804
805 /* A valid flag, CHARFORMAT2 structure minimally filled */
806 memset(&cf2, 0, sizeof(cf2));
807 cf2.cbSize = sizeof(CHARFORMAT2A);
808 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
809 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
810 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
811 ok(rc == FALSE, "Should not be able to undo here.\n");
812 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
813
814 /* A valid flag, CHARFORMAT2 structure minimally filled */
815 memset(&cf2, 0, sizeof(cf2));
816 cf2.cbSize = sizeof(CHARFORMAT2A);
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (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_WORD, (LPARAM)&cf2);
827 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
828 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
829 todo_wine ok(rc == TRUE, "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_ALL, (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 == TRUE, "Should not be able to undo here.\n");
839 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
840
841 cf2.cbSize = sizeof(CHARFORMAT2A);
842 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
843
844 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
845 cf2.cbSize = sizeof(CHARFORMAT2A);
846 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
847 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
848 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
849
850 /* wParam==0 is default char format, does not set modify */
851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
852 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
853 ok(rc == 0, "Text marked as modified, expected not modified!\n");
854 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
855 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
856 if (! rtl)
857 {
858 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
859 ok(rc == 0, "Text marked as modified, expected not modified!\n");
860 }
861 else
862 skip("RTL language found\n");
863
864 /* wParam==SCF_SELECTION sets modify if nonempty selection */
865 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
866 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
867 ok(rc == 0, "Text marked as modified, expected not modified!\n");
868 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
869 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
870 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
871 ok(rc == 0, "Text marked as modified, expected not modified!\n");
872
873 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
874 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
875 ok(rc == 0, "Text marked as modified, expected not modified!\n");
876 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
877 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
878 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
879 ok(rc == 0, "Text marked as modified, expected not modified!\n");
880 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
881 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
882 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
883 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
884 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
885
886 /* wParam==SCF_ALL sets modify regardless of whether text is present */
887 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
888 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
889 ok(rc == 0, "Text marked as modified, expected not modified!\n");
890 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
891 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
892 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
893 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
894
895 DestroyWindow(hwndRichEdit);
896
897 /* EM_GETCHARFORMAT tests */
898 for (i = 0; tested_effects[i]; i++)
899 {
900 hwndRichEdit = new_richedit(NULL);
901 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
902
903 /* Need to set a TrueType font to get consistent CFM_BOLD results */
904 memset(&cf2, 0, sizeof(CHARFORMAT2A));
905 cf2.cbSize = sizeof(CHARFORMAT2A);
906 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
907 cf2.dwEffects = 0;
908 strcpy(cf2.szFaceName, "Courier New");
909 cf2.wWeight = FW_DONTCARE;
910 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
911
912 memset(&cf2, 0, sizeof(CHARFORMAT2A));
913 cf2.cbSize = sizeof(CHARFORMAT2A);
914 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
915 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
916 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
917 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
918 ||
919 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
920 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
921 ok((cf2.dwEffects & tested_effects[i]) == 0,
922 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
923
924 memset(&cf2, 0, sizeof(CHARFORMAT2A));
925 cf2.cbSize = sizeof(CHARFORMAT2A);
926 cf2.dwMask = tested_effects[i];
927 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
928 cf2.dwMask = CFM_SUPERSCRIPT;
929 cf2.dwEffects = tested_effects[i];
930 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
931 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
932
933 memset(&cf2, 0, sizeof(CHARFORMAT2A));
934 cf2.cbSize = sizeof(CHARFORMAT2A);
935 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
936 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
937 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
938 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
939 ||
940 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
941 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
942 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
943 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
944
945 memset(&cf2, 0, sizeof(CHARFORMAT2A));
946 cf2.cbSize = sizeof(CHARFORMAT2A);
947 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
948 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
949 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
950 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
951 ||
952 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
953 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
954 ok((cf2.dwEffects & tested_effects[i]) == 0,
955 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
956
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
963 ||
964 (cf2.dwMask & tested_effects[i]) == 0),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
966
967 DestroyWindow(hwndRichEdit);
968 }
969
970 for (i = 0; tested_effects[i]; i++)
971 {
972 hwndRichEdit = new_richedit(NULL);
973 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
974
975 /* Need to set a TrueType font to get consistent CFM_BOLD results */
976 memset(&cf2, 0, sizeof(CHARFORMAT2A));
977 cf2.cbSize = sizeof(CHARFORMAT2A);
978 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
979 cf2.dwEffects = 0;
980 strcpy(cf2.szFaceName, "Courier New");
981 cf2.wWeight = FW_DONTCARE;
982 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
983
984 memset(&cf2, 0, sizeof(CHARFORMAT2A));
985 cf2.cbSize = sizeof(CHARFORMAT2A);
986 cf2.dwMask = tested_effects[i];
987 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
988 cf2.dwMask = CFM_SUPERSCRIPT;
989 cf2.dwEffects = tested_effects[i];
990 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
991 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
992
993 memset(&cf2, 0, sizeof(CHARFORMAT2A));
994 cf2.cbSize = sizeof(CHARFORMAT2A);
995 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
996 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
997 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
998 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
999 ||
1000 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1001 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1002 ok((cf2.dwEffects & tested_effects[i]) == 0,
1003 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1004
1005 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1006 cf2.cbSize = sizeof(CHARFORMAT2A);
1007 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1008 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1009 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1010 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1011 ||
1012 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1013 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1014 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1015 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1016
1017 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018 cf2.cbSize = sizeof(CHARFORMAT2A);
1019 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1020 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1023 ||
1024 (cf2.dwMask & tested_effects[i]) == 0),
1025 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1026 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1027 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1028
1029 DestroyWindow(hwndRichEdit);
1030 }
1031
1032 /* Effects applied on an empty selection should take effect when selection is
1033 replaced with text */
1034 hwndRichEdit = new_richedit(NULL);
1035 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1036 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1037
1038 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1039 cf2.cbSize = sizeof(CHARFORMAT2A);
1040 cf2.dwMask = CFM_BOLD;
1041 cf2.dwEffects = CFE_BOLD;
1042 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1043
1044 /* Selection is now nonempty */
1045 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1046
1047 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1048 cf2.cbSize = sizeof(CHARFORMAT2A);
1049 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1050 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1051
1052 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1053 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1054 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1055 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1056
1057
1058 /* Set two effects on an empty selection */
1059 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1061
1062 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063 cf2.cbSize = sizeof(CHARFORMAT2A);
1064 cf2.dwMask = CFM_BOLD;
1065 cf2.dwEffects = CFE_BOLD;
1066 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067 cf2.dwMask = CFM_ITALIC;
1068 cf2.dwEffects = CFE_ITALIC;
1069 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1070
1071 /* Selection is now nonempty */
1072 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1073
1074 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1075 cf2.cbSize = sizeof(CHARFORMAT2A);
1076 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1077 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1078
1079 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1080 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1081 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1082 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1083
1084 /* Setting the (empty) selection to exactly the same place as before should
1085 NOT clear the insertion style! */
1086 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1087 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1088
1089 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1090 cf2.cbSize = sizeof(CHARFORMAT2A);
1091 cf2.dwMask = CFM_BOLD;
1092 cf2.dwEffects = CFE_BOLD;
1093 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1094
1095 /* Empty selection in same place, insert style should NOT be forgotten here. */
1096 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1097
1098 /* Selection is now nonempty */
1099 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1100
1101 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1102 cf2.cbSize = sizeof(CHARFORMAT2A);
1103 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1104 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1105
1106 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1107 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1108 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1109 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1110
1111 /* Ditto with EM_EXSETSEL */
1112 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1113 cr.cpMin = 2; cr.cpMax = 2;
1114 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1115
1116 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1117 cf2.cbSize = sizeof(CHARFORMAT2A);
1118 cf2.dwMask = CFM_BOLD;
1119 cf2.dwEffects = CFE_BOLD;
1120 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1121
1122 /* Empty selection in same place, insert style should NOT be forgotten here. */
1123 cr.cpMin = 2; cr.cpMax = 2;
1124 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1125
1126 /* Selection is now nonempty */
1127 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1128
1129 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130 cf2.cbSize = sizeof(CHARFORMAT2A);
1131 cr.cpMin = 2; cr.cpMax = 6;
1132 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1133 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1134
1135 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1136 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1137 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1138 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1139
1140 DestroyWindow(hwndRichEdit);
1141 }
1142
1143 static void test_EM_SETTEXTMODE(void)
1144 {
1145 HWND hwndRichEdit = new_richedit(NULL);
1146 CHARFORMAT2A cf2, cf2test;
1147 CHARRANGE cr;
1148 int rc = 0;
1149
1150 /*Attempt to use mutually exclusive modes*/
1151 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1152 ok(rc == E_INVALIDARG,
1153 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1154
1155 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1156 /*Insert text into the control*/
1157
1158 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1159
1160 /*Attempt to change the control to plain text mode*/
1161 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1162 ok(rc == E_UNEXPECTED,
1163 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1164
1165 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1166 If rich text is pasted, it should have the same formatting as the rest
1167 of the text in the control*/
1168
1169 /*Italicize the text
1170 *NOTE: If the default text was already italicized, the test will simply
1171 reverse; in other words, it will copy a regular "wine" into a plain
1172 text window that uses an italicized format*/
1173 cf2.cbSize = sizeof(CHARFORMAT2A);
1174 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1175
1176 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1177 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1178
1179 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1180 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1181
1182 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1183 however, SCF_ALL has been implemented*/
1184 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1185 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1186
1187 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1188 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1189
1190 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1191
1192 /*Select the string "wine"*/
1193 cr.cpMin = 0;
1194 cr.cpMax = 4;
1195 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1196
1197 /*Copy the italicized "wine" to the clipboard*/
1198 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1199
1200 /*Reset the formatting to default*/
1201 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1202 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1203 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1204
1205 /*Clear the text in the control*/
1206 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1207
1208 /*Switch to Plain Text Mode*/
1209 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1210 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1211
1212 /*Input "wine" again in normal format*/
1213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1214
1215 /*Paste the italicized "wine" into the control*/
1216 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1217
1218 /*Select a character from the first "wine" string*/
1219 cr.cpMin = 2;
1220 cr.cpMax = 3;
1221 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1222
1223 /*Retrieve its formatting*/
1224 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1225
1226 /*Select a character from the second "wine" string*/
1227 cr.cpMin = 5;
1228 cr.cpMax = 6;
1229 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1230
1231 /*Retrieve its formatting*/
1232 cf2test.cbSize = sizeof(CHARFORMAT2A);
1233 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1234
1235 /*Compare the two formattings*/
1236 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1237 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1238 cf2.dwEffects, cf2test.dwEffects);
1239 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1240 printing "wine" in the current format(normal)
1241 pasting "wine" from the clipboard(italicized)
1242 comparing the two formats(should differ)*/
1243
1244 /*Attempt to switch with text in control*/
1245 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1246 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1247
1248 /*Clear control*/
1249 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1250
1251 /*Switch into Rich Text mode*/
1252 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1253 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1254
1255 /*Print "wine" in normal formatting into the control*/
1256 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1257
1258 /*Paste italicized "wine" into the control*/
1259 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1260
1261 /*Select text from the first "wine" string*/
1262 cr.cpMin = 1;
1263 cr.cpMax = 3;
1264 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1265
1266 /*Retrieve its formatting*/
1267 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1268
1269 /*Select text from the second "wine" string*/
1270 cr.cpMin = 6;
1271 cr.cpMax = 7;
1272 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1273
1274 /*Retrieve its formatting*/
1275 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1276
1277 /*Test that the two formattings are not the same*/
1278 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1279 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1280 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1281
1282 DestroyWindow(hwndRichEdit);
1283 }
1284
1285 static void test_SETPARAFORMAT(void)
1286 {
1287 HWND hwndRichEdit = new_richedit(NULL);
1288 PARAFORMAT2 fmt;
1289 HRESULT ret;
1290 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1291 fmt.cbSize = sizeof(PARAFORMAT2);
1292 fmt.dwMask = PFM_ALIGNMENT;
1293 fmt.wAlignment = PFA_LEFT;
1294
1295 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1296 ok(ret != 0, "expected non-zero got %d\n", ret);
1297
1298 fmt.cbSize = sizeof(PARAFORMAT2);
1299 fmt.dwMask = -1;
1300 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1301 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1302 * between richedit different native builds of riched20.dll
1303 * used on different Windows versions. */
1304 ret &= ~PFM_TABLEROWDELIMITER;
1305 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1306
1307 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1308 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1309
1310 DestroyWindow(hwndRichEdit);
1311 }
1312
1313 static void test_TM_PLAINTEXT(void)
1314 {
1315 /*Tests plain text properties*/
1316
1317 HWND hwndRichEdit = new_richedit(NULL);
1318 CHARFORMAT2A cf2, cf2test;
1319 CHARRANGE cr;
1320 int rc = 0;
1321
1322 /*Switch to plain text mode*/
1323
1324 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1325 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1326
1327 /*Fill control with text*/
1328
1329 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1330
1331 /*Select some text and bold it*/
1332
1333 cr.cpMin = 10;
1334 cr.cpMax = 20;
1335 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1336 cf2.cbSize = sizeof(CHARFORMAT2A);
1337 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1338
1339 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1340 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1341
1342 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1343 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1344
1345 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1346 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1347
1348 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1349 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1350
1351 /*Get the formatting of those characters*/
1352
1353 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1354
1355 /*Get the formatting of some other characters*/
1356 cf2test.cbSize = sizeof(CHARFORMAT2A);
1357 cr.cpMin = 21;
1358 cr.cpMax = 30;
1359 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1360 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1361
1362 /*Test that they are the same as plain text allows only one formatting*/
1363
1364 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1365 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1366 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1367
1368 /*Fill the control with a "wine" string, which when inserted will be bold*/
1369
1370 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1371
1372 /*Copy the bolded "wine" string*/
1373
1374 cr.cpMin = 0;
1375 cr.cpMax = 4;
1376 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1377 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1378
1379 /*Swap back to rich text*/
1380
1381 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1382 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1383
1384 /*Set the default formatting to bold italics*/
1385
1386 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1387 cf2.dwMask |= CFM_ITALIC;
1388 cf2.dwEffects ^= CFE_ITALIC;
1389 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1390 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1391
1392 /*Set the text in the control to "wine", which will be bold and italicized*/
1393
1394 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1395
1396 /*Paste the plain text "wine" string, which should take the insert
1397 formatting, which at the moment is bold italics*/
1398
1399 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1400
1401 /*Select the first "wine" string and retrieve its formatting*/
1402
1403 cr.cpMin = 1;
1404 cr.cpMax = 3;
1405 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1406 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1407
1408 /*Select the second "wine" string and retrieve its formatting*/
1409
1410 cr.cpMin = 5;
1411 cr.cpMax = 7;
1412 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1413 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1414
1415 /*Compare the two formattings. They should be the same.*/
1416
1417 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1418 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1419 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1420 DestroyWindow(hwndRichEdit);
1421 }
1422
1423 static void test_WM_GETTEXT(void)
1424 {
1425 HWND hwndRichEdit = new_richedit(NULL);
1426 static const char text[] = "Hello. My name is RichEdit!";
1427 static const char text2[] = "Hello. My name is RichEdit!\r";
1428 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1429 char buffer[1024] = {0};
1430 int result;
1431
1432 /* Baseline test with normal-sized buffer */
1433 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1434 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1435 ok(result == lstrlenA(buffer),
1436 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1437 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1438 result = strcmp(buffer,text);
1439 ok(result == 0,
1440 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1441
1442 /* Test for returned value of WM_GETTEXTLENGTH */
1443 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1444 ok(result == lstrlenA(text),
1445 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1446 result, lstrlenA(text));
1447
1448 /* Test for behavior in overflow case */
1449 memset(buffer, 0, 1024);
1450 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1451 ok(result == 0 ||
1452 result == lstrlenA(text) - 1, /* XP, win2k3 */
1453 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1454 result = strcmp(buffer,text);
1455 if (result)
1456 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1457 ok(result == 0,
1458 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1459
1460 /* Baseline test with normal-sized buffer and carriage return */
1461 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1462 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1463 ok(result == lstrlenA(buffer),
1464 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1465 result = strcmp(buffer,text2_after);
1466 ok(result == 0,
1467 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1468
1469 /* Test for returned value of WM_GETTEXTLENGTH */
1470 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1471 ok(result == lstrlenA(text2_after),
1472 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1473 result, lstrlenA(text2_after));
1474
1475 /* Test for behavior of CRLF conversion in case of overflow */
1476 memset(buffer, 0, 1024);
1477 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1478 ok(result == 0 ||
1479 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1480 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1481 result = strcmp(buffer,text2);
1482 if (result)
1483 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1484 ok(result == 0,
1485 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1486
1487 DestroyWindow(hwndRichEdit);
1488 }
1489
1490 static void test_EM_GETTEXTRANGE(void)
1491 {
1492 HWND hwndRichEdit = new_richedit(NULL);
1493 const char * text1 = "foo bar\r\nfoo bar";
1494 const char * text2 = "foo bar\rfoo bar";
1495 const char * expect = "bar\rfoo";
1496 char buffer[1024] = {0};
1497 LRESULT result;
1498 TEXTRANGEA textRange;
1499
1500 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1501
1502 textRange.lpstrText = buffer;
1503 textRange.chrg.cpMin = 4;
1504 textRange.chrg.cpMax = 11;
1505 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1506 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1507 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1508
1509 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1510
1511 textRange.lpstrText = buffer;
1512 textRange.chrg.cpMin = 4;
1513 textRange.chrg.cpMax = 11;
1514 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1515 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1516 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1517
1518 /* cpMax of text length is used instead of -1 in this case */
1519 textRange.lpstrText = buffer;
1520 textRange.chrg.cpMin = 0;
1521 textRange.chrg.cpMax = -1;
1522 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1523 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1524 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1525
1526 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1527 textRange.lpstrText = buffer;
1528 textRange.chrg.cpMin = -1;
1529 textRange.chrg.cpMax = 1;
1530 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1531 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1532 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1533
1534 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1535 textRange.lpstrText = buffer;
1536 textRange.chrg.cpMin = 1;
1537 textRange.chrg.cpMax = -1;
1538 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1539 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1540 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1541
1542 /* no end character is copied if cpMax - cpMin < 0 */
1543 textRange.lpstrText = buffer;
1544 textRange.chrg.cpMin = 5;
1545 textRange.chrg.cpMax = 5;
1546 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1547 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1548 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1549
1550 /* cpMax of text length is used if cpMax > text length*/
1551 textRange.lpstrText = buffer;
1552 textRange.chrg.cpMin = 0;
1553 textRange.chrg.cpMax = 1000;
1554 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1555 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1556 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1557
1558 /* Test with multibyte character */
1559 if (!is_lang_japanese)
1560 skip("Skip multibyte character tests on non-Japanese platform\n");
1561 else
1562 {
1563 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1564 textRange.chrg.cpMin = 4;
1565 textRange.chrg.cpMax = 8;
1566 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1567 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1568 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1569 }
1570
1571 DestroyWindow(hwndRichEdit);
1572 }
1573
1574 static void test_EM_GETSELTEXT(void)
1575 {
1576 HWND hwndRichEdit = new_richedit(NULL);
1577 const char * text1 = "foo bar\r\nfoo bar";
1578 const char * text2 = "foo bar\rfoo bar";
1579 const char * expect = "bar\rfoo";
1580 char buffer[1024] = {0};
1581 LRESULT result;
1582
1583 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1584
1585 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1586 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1587 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1588 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1589
1590 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1591
1592 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1593 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1594 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1595 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1596
1597 /* Test with multibyte character */
1598 if (!is_lang_japanese)
1599 skip("Skip multibyte character tests on non-Japanese platform\n");
1600 else
1601 {
1602 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1603 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1604 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1605 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1606 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1607 }
1608
1609 DestroyWindow(hwndRichEdit);
1610 }
1611
1612 /* FIXME: need to test unimplemented options and robustly test wparam */
1613 static void test_EM_SETOPTIONS(void)
1614 {
1615 HWND hwndRichEdit;
1616 static const char text[] = "Hello. My name is RichEdit!";
1617 char buffer[1024] = {0};
1618 DWORD dwStyle, options, oldOptions;
1619 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1620 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1621 ES_SELECTIONBAR|ES_VERTICAL;
1622
1623 /* Test initial options. */
1624 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1625 0, 0, 200, 60, NULL, NULL,
1626 hmoduleRichEdit, NULL);
1627 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1628 RICHEDIT_CLASS20A, (int) GetLastError());
1629 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1630 ok(options == 0, "Incorrect initial options %x\n", options);
1631 DestroyWindow(hwndRichEdit);
1632
1633 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1634 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1635 0, 0, 200, 60, NULL, NULL,
1636 hmoduleRichEdit, NULL);
1637 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1638 RICHEDIT_CLASS20A, (int) GetLastError());
1639 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1640 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1641 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1642 "Incorrect initial options %x\n", options);
1643
1644 /* NEGATIVE TESTING - NO OPTIONS SET */
1645 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1646 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1647
1648 /* testing no readonly by sending 'a' to the control*/
1649 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1650 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1651 ok(buffer[0]=='a',
1652 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1653 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1654
1655 /* READONLY - sending 'a' to the control */
1656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1657 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1658 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1659 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1660 ok(buffer[0]==text[0],
1661 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1662
1663 /* EM_SETOPTIONS changes the window style, but changing the
1664 * window style does not change the options. */
1665 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1666 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1667 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1668 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1669 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1670 /* Confirm that the text is still read only. */
1671 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1672 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1673 ok(buffer[0]==text[0],
1674 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1675
1676 oldOptions = options;
1677 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1678 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1679 ok(options == oldOptions,
1680 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1681
1682 DestroyWindow(hwndRichEdit);
1683 }
1684
1685 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1686 {
1687 CHARFORMAT2A text_format;
1688 text_format.cbSize = sizeof(text_format);
1689 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1690 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1691 return (text_format.dwEffects & CFE_LINK) != 0;
1692 }
1693
1694 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1695 {
1696 BOOL link_present = FALSE;
1697
1698 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1699 if (is_url)
1700 { /* control text is url; should get CFE_LINK */
1701 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1702 }
1703 else
1704 {
1705 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1706 }
1707 }
1708
1709 static HWND new_static_wnd(HWND parent) {
1710 return new_window("Static", 0, parent);
1711 }
1712
1713 static void test_EM_AUTOURLDETECT(void)
1714 {
1715 /* DO NOT change the properties of the first two elements. To shorten the
1716 tests, all tests after WM_SETTEXT test just the first two elements -
1717 one non-URL and one URL */
1718 struct urls_s {
1719 const char *text;
1720 BOOL is_url;
1721 } urls[12] = {
1722 {"winehq.org", FALSE},
1723 {"http://www.winehq.org", TRUE},
1724 {"http//winehq.org", FALSE},
1725 {"ww.winehq.org", FALSE},
1726 {"www.winehq.org", TRUE},
1727 {"ftp://192.168.1.1", TRUE},
1728 {"ftp//192.168.1.1", FALSE},
1729 {"mailto:your@email.com", TRUE},
1730 {"prospero:prosperoserver", TRUE},
1731 {"telnet:test", TRUE},
1732 {"news:newserver", TRUE},
1733 {"wais:waisserver", TRUE}
1734 };
1735
1736 int i, j;
1737 int urlRet=-1;
1738 HWND hwndRichEdit, parent;
1739
1740 /* All of the following should cause the URL to be detected */
1741 const char * templates_delim[] = {
1742 "This is some text with X on it",
1743 "This is some text with (X) on it",
1744 "This is some text with X\r on it",
1745 "This is some text with ---X--- on it",
1746 "This is some text with \"X\" on it",
1747 "This is some text with 'X' on it",
1748 "This is some text with 'X' on it",
1749 "This is some text with :X: on it",
1750
1751 "This text ends with X",
1752
1753 "This is some text with X) on it",
1754 "This is some text with X--- on it",
1755 "This is some text with X\" on it",
1756 "This is some text with X' on it",
1757 "This is some text with X: on it",
1758
1759 "This is some text with (X on it",
1760 "This is some text with \rX on it",
1761 "This is some text with ---X on it",
1762 "This is some text with \"X on it",
1763 "This is some text with 'X on it",
1764 "This is some text with :X on it",
1765 };
1766 /* None of these should cause the URL to be detected */
1767 const char * templates_non_delim[] = {
1768 "This is some text with |X| on it",
1769 "This is some text with *X* on it",
1770 "This is some text with /X/ on it",
1771 "This is some text with +X+ on it",
1772 "This is some text with %X% on it",
1773 "This is some text with #X# on it",
1774 "This is some text with @X@ on it",
1775 "This is some text with \\X\\ on it",
1776 "This is some text with |X on it",
1777 "This is some text with *X on it",
1778 "This is some text with /X on it",
1779 "This is some text with +X on it",
1780 "This is some text with %X on it",
1781 "This is some text with #X on it",
1782 "This is some text with @X on it",
1783 "This is some text with \\X on it",
1784 "This is some text with _X on it",
1785 };
1786 /* All of these cause the URL detection to be extended by one more byte,
1787 thus demonstrating that the tested character is considered as part
1788 of the URL. */
1789 const char * templates_xten_delim[] = {
1790 "This is some text with X| on it",
1791 "This is some text with X* on it",
1792 "This is some text with X/ on it",
1793 "This is some text with X+ on it",
1794 "This is some text with X% on it",
1795 "This is some text with X# on it",
1796 "This is some text with X@ on it",
1797 "This is some text with X\\ on it",
1798 "This is some text with X_ on it",
1799 };
1800 /* These delims act as neutral breaks. Whether the url is ended
1801 or not depends on the next non-neutral character. We'll test
1802 with Y unchanged, in which case the url should include the
1803 deliminator and the Y. We'll also test with the Y changed
1804 to a space, in which case the url stops before the
1805 deliminator. */
1806 const char * templates_neutral_delim[] = {
1807 "This is some text with X-Y on it",
1808 "This is some text with X--Y on it",
1809 "This is some text with X!Y on it",
1810 "This is some text with X[Y on it",
1811 "This is some text with X]Y on it",
1812 "This is some text with X{Y on it",
1813 "This is some text with X}Y on it",
1814 "This is some text with X(Y on it",
1815 "This is some text with X)Y on it",
1816 "This is some text with X\"Y on it",
1817 "This is some text with X;Y on it",
1818 "This is some text with X:Y on it",
1819 "This is some text with X'Y on it",
1820 "This is some text with X?Y on it",
1821 "This is some text with X<Y on it",
1822 "This is some text with X>Y on it",
1823 "This is some text with X.Y on it",
1824 "This is some text with X,Y on it",
1825 };
1826 char buffer[1024];
1827
1828 parent = new_static_wnd(NULL);
1829 hwndRichEdit = new_richedit(parent);
1830 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1831 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1832 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1833 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1834 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1835 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1836 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1837 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1838 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1839 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1840 /* for each url, check the text to see if CFE_LINK effect is present */
1841 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1842
1843 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1844 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1845 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1846
1847 /* Link detection should happen immediately upon WM_SETTEXT */
1848 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1849 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1850 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1851 }
1852 DestroyWindow(hwndRichEdit);
1853
1854 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1855 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1856 hwndRichEdit = new_richedit(parent);
1857
1858 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1859 char * at_pos;
1860 int at_offset;
1861 int end_offset;
1862
1863 at_pos = strchr(templates_delim[j], 'X');
1864 at_offset = at_pos - templates_delim[j];
1865 memcpy(buffer, templates_delim[j], at_offset);
1866 buffer[at_offset] = '\0';
1867 strcat(buffer, urls[i].text);
1868 strcat(buffer, templates_delim[j] + at_offset + 1);
1869 end_offset = at_offset + strlen(urls[i].text);
1870
1871 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1872 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1873
1874 /* This assumes no templates start with the URL itself, and that they
1875 have at least two characters before the URL text */
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1880 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1881 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1882
1883 if (urls[i].is_url)
1884 {
1885 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1886 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1887 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1888 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1889 }
1890 else
1891 {
1892 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1893 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1894 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1895 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1896 }
1897 if (buffer[end_offset] != '\0')
1898 {
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1901 if (buffer[end_offset +1] != '\0')
1902 {
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1905 }
1906 }
1907 }
1908
1909 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1910 char * at_pos;
1911 int at_offset;
1912 int end_offset;
1913
1914 at_pos = strchr(templates_non_delim[j], 'X');
1915 at_offset = at_pos - templates_non_delim[j];
1916 memcpy(buffer, templates_non_delim[j], at_offset);
1917 buffer[at_offset] = '\0';
1918 strcat(buffer, urls[i].text);
1919 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1920 end_offset = at_offset + strlen(urls[i].text);
1921
1922 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1923 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1924
1925 /* This assumes no templates start with the URL itself, and that they
1926 have at least two characters before the URL text */
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1931 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1932 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1933
1934 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1935 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1938 if (buffer[end_offset] != '\0')
1939 {
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1942 if (buffer[end_offset +1] != '\0')
1943 {
1944 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1945 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1946 }
1947 }
1948 }
1949
1950 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1951 char * at_pos;
1952 int at_offset;
1953 int end_offset;
1954
1955 at_pos = strchr(templates_xten_delim[j], 'X');
1956 at_offset = at_pos - templates_xten_delim[j];
1957 memcpy(buffer, templates_xten_delim[j], at_offset);
1958 buffer[at_offset] = '\0';
1959 strcat(buffer, urls[i].text);
1960 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1961 end_offset = at_offset + strlen(urls[i].text);
1962
1963 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1964 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1965
1966 /* This assumes no templates start with the URL itself, and that they
1967 have at least two characters before the URL text */
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1970 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1971 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1972 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1973 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1974
1975 if (urls[i].is_url)
1976 {
1977 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1978 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1979 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1980 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1981 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1982 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1983 }
1984 else
1985 {
1986 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1987 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1992 }
1993 if (buffer[end_offset +1] != '\0')
1994 {
1995 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1996 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1997 if (buffer[end_offset +2] != '\0')
1998 {
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2001 }
2002 }
2003 }
2004
2005 for (j = 0; j < sizeof(templates_neutral_delim) / sizeof(const char *); j++) {
2006 char * at_pos, * end_pos;
2007 int at_offset;
2008 int end_offset;
2009
2010 if (!urls[i].is_url) continue;
2011
2012 at_pos = strchr(templates_neutral_delim[j], 'X');
2013 at_offset = at_pos - templates_neutral_delim[j];
2014 memcpy(buffer, templates_neutral_delim[j], at_offset);
2015 buffer[at_offset] = '\0';
2016 strcat(buffer, urls[i].text);
2017 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2018
2019 end_pos = strchr(buffer, 'Y');
2020 end_offset = end_pos - buffer;
2021
2022 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2023 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2024
2025 /* This assumes no templates start with the URL itself, and that they
2026 have at least two characters before the URL text */
2027 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2028 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2029 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2030 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2031 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2032 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2033
2034 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2035 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2036 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2037 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2038 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2039 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2040
2041 *end_pos = ' ';
2042
2043 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2044 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2045
2046 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2047 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2048 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2049 "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2050 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2051 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2052 }
2053
2054 DestroyWindow(hwndRichEdit);
2055 hwndRichEdit = NULL;
2056 }
2057
2058 /* Test detection of URLs within normal text - WM_CHAR case. */
2059 /* Test only the first two URL examples for brevity */
2060 for (i = 0; i < 2; i++) {
2061 hwndRichEdit = new_richedit(parent);
2062
2063 /* Also for brevity, test only the first three delimiters */
2064 for (j = 0; j < 3; j++) {
2065 char * at_pos;
2066 int at_offset;
2067 int end_offset;
2068 int u, v;
2069
2070 at_pos = strchr(templates_delim[j], 'X');
2071 at_offset = at_pos - templates_delim[j];
2072 end_offset = at_offset + strlen(urls[i].text);
2073
2074 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2075 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2076 for (u = 0; templates_delim[j][u]; u++) {
2077 if (templates_delim[j][u] == '\r') {
2078 simulate_typing_characters(hwndRichEdit, "\r");
2079 } else if (templates_delim[j][u] != 'X') {
2080 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2081 } else {
2082 for (v = 0; urls[i].text[v]; v++) {
2083 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2084 }
2085 }
2086 }
2087 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2088
2089 /* This assumes no templates start with the URL itself, and that they
2090 have at least two characters before the URL text */
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2097
2098 if (urls[i].is_url)
2099 {
2100 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2101 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2102 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2103 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2104 }
2105 else
2106 {
2107 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2108 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2109 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2110 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2111 }
2112 if (buffer[end_offset] != '\0')
2113 {
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2116 if (buffer[end_offset +1] != '\0')
2117 {
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2120 }
2121 }
2122
2123 /* The following will insert a paragraph break after the first character
2124 of the URL candidate, thus breaking the URL. It is expected that the
2125 CFE_LINK attribute should break across both pieces of the URL */
2126 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2127 simulate_typing_characters(hwndRichEdit, "\r");
2128 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2129
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2132 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2133 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2134 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2135 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2136
2137 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2138 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2139 /* end_offset moved because of paragraph break */
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, end_offset+1, buffer);
2142 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2143 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2144 {
2145 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2147 if (buffer[end_offset +2] != '\0')
2148 {
2149 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2150 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2151 }
2152 }
2153
2154 /* The following will remove the just-inserted paragraph break, thus
2155 restoring the URL */
2156 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2157 simulate_typing_characters(hwndRichEdit, "\b");
2158 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2159
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2162 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2163 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2164 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2165 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2166
2167 if (urls[i].is_url)
2168 {
2169 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2170 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2171 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2172 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2173 }
2174 else
2175 {
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2178 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2179 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2180 }
2181 if (buffer[end_offset] != '\0')
2182 {
2183 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2184 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2185 if (buffer[end_offset +1] != '\0')
2186 {
2187 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2188 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2189 }
2190 }
2191 }
2192 DestroyWindow(hwndRichEdit);
2193 hwndRichEdit = NULL;
2194 }
2195
2196 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2197 /* Test just the first two URL examples for brevity */
2198 for (i = 0; i < 2; i++) {
2199 SETTEXTEX st;
2200
2201 hwndRichEdit = new_richedit(parent);
2202
2203 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2204 be detected:
2205 1) Set entire text, a la WM_SETTEXT
2206 2) Set a selection of the text to the URL
2207 3) Set a portion of the text at a time, which eventually results in
2208 an URL
2209 All of them should give equivalent results
2210 */
2211
2212 /* Set entire text in one go, like WM_SETTEXT */
2213 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2214 char * at_pos;
2215 int at_offset;
2216 int end_offset;
2217
2218 st.codepage = CP_ACP;
2219 st.flags = ST_DEFAULT;
2220
2221 at_pos = strchr(templates_delim[j], 'X');
2222 at_offset = at_pos - templates_delim[j];
2223 memcpy(buffer, templates_delim[j], at_offset);
2224 buffer[at_offset] = '\0';
2225 strcat(buffer, urls[i].text);
2226 strcat(buffer, templates_delim[j] + at_offset + 1);
2227 end_offset = at_offset + strlen(urls[i].text);
2228
2229 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2230 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2231
2232 /* This assumes no templates start with the URL itself, and that they
2233 have at least two characters before the URL text */
2234 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2235 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2236 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2237 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2238 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2239 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2240
2241 if (urls[i].is_url)
2242 {
2243 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2244 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2245 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2246 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2247 }
2248 else
2249 {
2250 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2251 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2254 }
2255 if (buffer[end_offset] != '\0')
2256 {
2257 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2258 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2259 if (buffer[end_offset +1] != '\0')
2260 {
2261 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2262 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2263 }
2264 }
2265 }
2266
2267 /* Set selection with X to the URL */
2268 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2269 char * at_pos;
2270 int at_offset;
2271 int end_offset;
2272
2273 at_pos = strchr(templates_delim[j], 'X');
2274 at_offset = at_pos - templates_delim[j];
2275 end_offset = at_offset + strlen(urls[i].text);
2276
2277 st.codepage = CP_ACP;
2278 st.flags = ST_DEFAULT;
2279 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2280 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2281 st.flags = ST_SELECTION;
2282 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2283 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2284 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2285
2286 /* This assumes no templates start with the URL itself, and that they
2287 have at least two characters before the URL text */
2288 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2289 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2290 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2291 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2292 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2293 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2294
2295 if (urls[i].is_url)
2296 {
2297 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2298 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2299 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2300 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2301 }
2302 else
2303 {
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2305 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2308 }
2309 if (buffer[end_offset] != '\0')
2310 {
2311 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2312 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2313 if (buffer[end_offset +1] != '\0')
2314 {
2315 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2316 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2317 }
2318 }
2319 }
2320
2321 /* Set selection with X to the first character of the URL, then the rest */
2322 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2323 char * at_pos;
2324 int at_offset;
2325 int end_offset;
2326
2327 at_pos = strchr(templates_delim[j], 'X');
2328 at_offset = at_pos - templates_delim[j];
2329 end_offset = at_offset + strlen(urls[i].text);
2330
2331 strcpy(buffer, "YY");
2332 buffer[0] = urls[i].text[0];
2333
2334 st.codepage = CP_ACP;
2335 st.flags = ST_DEFAULT;
2336 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2337 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2338 st.flags = ST_SELECTION;
2339 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2340 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2341 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2342 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2343 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2344
2345 /* This assumes no templates start with the URL itself, and that they
2346 have at least two characters before the URL text */
2347 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2348 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2349 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2350 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2351 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2352 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2353
2354 if (urls[i].is_url)
2355 {
2356 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2357 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2358 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2359 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2360 }
2361 else
2362 {
2363 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2364 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2365 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2366 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2367 }
2368 if (buffer[end_offset] != '\0')
2369 {
2370 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2371 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2372 if (buffer[end_offset +1] != '\0')
2373 {
2374 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2375 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2376 }
2377 }
2378 }
2379
2380 DestroyWindow(hwndRichEdit);
2381 hwndRichEdit = NULL;
2382 }
2383
2384 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2385 /* Test just the first two URL examples for brevity */
2386 for (i = 0; i < 2; i++) {
2387 hwndRichEdit = new_richedit(parent);
2388
2389 /* Set selection with X to the URL */
2390 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2391 char * at_pos;
2392 int at_offset;
2393 int end_offset;
2394
2395 at_pos = strchr(templates_delim[j], 'X');
2396 at_offset = at_pos - templates_delim[j];
2397 end_offset = at_offset + strlen(urls[i].text);
2398
2399 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2400 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2401 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2402 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2403 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2404
2405 /* This assumes no templates start with the URL itself, and that they
2406 have at least two characters before the URL text */
2407 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2408 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2409 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2410 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2411 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2412 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2413
2414 if (urls[i].is_url)
2415 {
2416 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2417 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2418 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2419 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2420 }
2421 else
2422 {
2423 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2424 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2425 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2426 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2427 }
2428 if (buffer[end_offset] != '\0')
2429 {
2430 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2431 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2432 if (buffer[end_offset +1] != '\0')
2433 {
2434 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2435 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2436 }
2437 }
2438 }
2439
2440 /* Set selection with X to the first character of the URL, then the rest */
2441 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2442 char * at_pos;
2443 int at_offset;
2444 int end_offset;
2445
2446 at_pos = strchr(templates_delim[j], 'X');
2447 at_offset = at_pos - templates_delim[j];
2448 end_offset = at_offset + strlen(urls[i].text);
2449
2450 strcpy(buffer, "YY");
2451 buffer[0] = urls[i].text[0];
2452
2453 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2454 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2455 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2456 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2457 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2458 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2459 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2460
2461 /* This assumes no templates start with the URL itself, and that they
2462 have at least two characters before the URL text */
2463 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2464 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2465 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2466 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2467 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2468 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2469
2470 if (urls[i].is_url)
2471 {
2472 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2473 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2474 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2475 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2476 }
2477 else
2478 {
2479 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2480 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2481 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2482 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2483 }
2484 if (buffer[end_offset] != '\0')
2485 {
2486 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2487 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2488 if (buffer[end_offset +1] != '\0')
2489 {
2490 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2491 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2492 }
2493 }
2494 }
2495
2496 DestroyWindow(hwndRichEdit);
2497 hwndRichEdit = NULL;
2498 }
2499
2500 DestroyWindow(parent);
2501 }
2502
2503 static void test_EM_SCROLL(void)
2504 {
2505 int i, j;
2506 int r; /* return value */
2507 int expr; /* expected return value */
2508 HWND hwndRichEdit = new_richedit(NULL);
2509 int y_before, y_after; /* units of lines of text */
2510
2511 /* test a richedit box containing a single line of text */
2512 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2513 expr = 0x00010000;
2514 for (i = 0; i < 4; i++) {
2515 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2516
2517 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2518 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2519 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2520 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2521 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2522 "(i == %d)\n", y_after, i);
2523 }
2524
2525 /*
2526 * test a richedit box that will scroll. There are two general
2527 * cases: the case without any long lines and the case with a long
2528 * line.
2529 */
2530 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2531 if (i == 0)
2532 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2533 else
2534 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2535 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2536 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2537 "LONG LINE \nb\nc\nd\ne");
2538 for (j = 0; j < 12; j++) /* reset scroll position to top */
2539 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2540
2541 /* get first visible line */
2542 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2543 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2544
2545 /* get new current first visible line */
2546 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2547
2548 ok(((r & 0xffffff00) == 0x00010000) &&
2549 ((r & 0x000000ff) != 0x00000000),
2550 "EM_SCROLL page down didn't scroll by a small positive number of "
2551 "lines (r == 0x%08x)\n", r);
2552 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2553 "(line %d scrolled to line %d\n", y_before, y_after);
2554
2555 y_before = y_after;
2556
2557 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2558 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2559 ok(((r & 0xffffff00) == 0x0001ff00),
2560 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2561 "(r == 0x%08x)\n", r);
2562 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2563 "%d scrolled to line %d\n", y_before, y_after);
2564
2565 y_before = y_after;
2566
2567 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2568
2569 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2570
2571 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2572 "(r == 0x%08x)\n", r);
2573 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2574 "1 line (%d scrolled to %d)\n", y_before, y_after);
2575
2576 y_before = y_after;
2577
2578 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2579
2580 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2581
2582 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2583 "(r == 0x%08x)\n", r);
2584 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2585 "line (%d scrolled to %d)\n", y_before, y_after);
2586
2587 y_before = y_after;
2588
2589 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2590 SB_LINEUP, 0); /* lineup beyond top */
2591
2592 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2593
2594 ok(r == 0x00010000,
2595 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2596 ok(y_before == y_after,
2597 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2598
2599 y_before = y_after;
2600
2601 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2602 SB_PAGEUP, 0);/*page up beyond top */
2603
2604 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2605
2606 ok(r == 0x00010000,
2607 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2608 ok(y_before == y_after,
2609 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2610
2611 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2612 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2613 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2614 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2615 SB_PAGEDOWN, 0); /* page down beyond bot */
2616 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2617
2618 ok(r == 0x00010000,
2619 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2620 ok(y_before == y_after,
2621 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2622 y_before, y_after);
2623
2624 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2625 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2626 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2627
2628 ok(r == 0x00010000,
2629 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2630 ok(y_before == y_after,
2631 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2632 y_before, y_after);
2633 }
2634 DestroyWindow(hwndRichEdit);
2635 }
2636
2637 static unsigned int recursionLevel = 0;
2638 static unsigned int WM_SIZE_recursionLevel = 0;
2639 static BOOL bailedOutOfRecursion = FALSE;
2640 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2641
2642 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2643 {
2644 LRESULT r;
2645
2646 if (bailedOutOfRecursion) return 0;
2647 if (recursionLevel >= 32) {
2648 bailedOutOfRecursion = TRUE;
2649 return 0;
2650 }
2651
2652 recursionLevel++;
2653 switch (message) {
2654 case WM_SIZE:
2655 WM_SIZE_recursionLevel++;
2656 r = richeditProc(hwnd, message, wParam, lParam);
2657 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2658 ShowScrollBar(hwnd, SB_VERT, TRUE);
2659 WM_SIZE_recursionLevel--;
2660 break;
2661 default:
2662 r = richeditProc(hwnd, message, wParam, lParam);
2663 break;
2664 }
2665 recursionLevel--;
2666 return r;
2667 }
2668
2669 static void test_scrollbar_visibility(void)
2670 {
2671 HWND hwndRichEdit;
2672 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2673 SCROLLINFO si;
2674 WNDCLASSA cls;
2675 BOOL r;
2676
2677 /* These tests show that richedit should temporarily refrain from automatically
2678 hiding or showing its scrollbars (vertical at least) when an explicit request
2679 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2680 Some applications depend on forced showing (when otherwise richedit would
2681 hide the vertical scrollbar) and are thrown on an endless recursive loop
2682 if richedit auto-hides the scrollbar again. Apparently they never heard of
2683 the ES_DISABLENOSCROLL style... */
2684
2685 hwndRichEdit = new_richedit(NULL);
2686
2687 /* Test default scrollbar visibility behavior */
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2693 "Vertical scrollbar is visible, should be invisible.\n");
2694 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2695 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2696 si.nPage, si.nMin, si.nMax);
2697
2698 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2699 memset(&si, 0, sizeof(si));
2700 si.cbSize = sizeof(si);
2701 si.fMask = SIF_PAGE | SIF_RANGE;
2702 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2703 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2704 "Vertical scrollbar is visible, should be invisible.\n");
2705 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2706 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2707 si.nPage, si.nMin, si.nMax);
2708
2709 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2710 memset(&si, 0, sizeof(si));
2711 si.cbSize = sizeof(si);
2712 si.fMask = SIF_PAGE | SIF_RANGE;
2713 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2714 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2715 "Vertical scrollbar is invisible, should be visible.\n");
2716 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2717 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2718 si.nPage, si.nMin, si.nMax);
2719
2720 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2721 even though it hides the scrollbar */
2722 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2723 memset(&si, 0, sizeof(si));
2724 si.cbSize = sizeof(si);
2725 si.fMask = SIF_PAGE | SIF_RANGE;
2726 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2727 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2728 "Vertical scrollbar is visible, should be invisible.\n");
2729 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2730 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2731 si.nPage, si.nMin, si.nMax);
2732
2733 /* Setting non-scrolling text again does *not* reset scrollbar range */
2734 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2735 memset(&si, 0, sizeof(si));
2736 si.cbSize = sizeof(si);
2737 si.fMask = SIF_PAGE | SIF_RANGE;
2738 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2739 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2740 "Vertical scrollbar is visible, should be invisible.\n");
2741 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2742 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2743 si.nPage, si.nMin, si.nMax);
2744
2745 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2746 memset(&si, 0, sizeof(si));
2747 si.cbSize = sizeof(si);
2748 si.fMask = SIF_PAGE | SIF_RANGE;
2749 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2750 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2751 "Vertical scrollbar is visible, should be invisible.\n");
2752 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2753 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2754 si.nPage, si.nMin, si.nMax);
2755
2756 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2757 memset(&si, 0, sizeof(si));
2758 si.cbSize = sizeof(si);
2759 si.fMask = SIF_PAGE | SIF_RANGE;
2760 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2761 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2762 "Vertical scrollbar is visible, should be invisible.\n");
2763 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2764 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2765 si.nPage, si.nMin, si.nMax);
2766
2767 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773 "Vertical scrollbar is visible, should be invisible.\n");
2774 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776 si.nPage, si.nMin, si.nMax);
2777
2778 DestroyWindow(hwndRichEdit);
2779
2780 /* Test again, with ES_DISABLENOSCROLL style */
2781 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2782
2783 /* Test default scrollbar visibility behavior */
2784 memset(&si, 0, sizeof(si));
2785 si.cbSize = sizeof(si);
2786 si.fMask = SIF_PAGE | SIF_RANGE;
2787 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2788 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2789 "Vertical scrollbar is invisible, should be visible.\n");
2790 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2791 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2792 si.nPage, si.nMin, si.nMax);
2793
2794 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2795 memset(&si, 0, sizeof(si));
2796 si.cbSize = sizeof(si);
2797 si.fMask = SIF_PAGE | SIF_RANGE;
2798 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2799 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2800 "Vertical scrollbar is invisible, should be visible.\n");
2801 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2802 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2803 si.nPage, si.nMin, si.nMax);
2804
2805 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2806 memset(&si, 0, sizeof(si));
2807 si.cbSize = sizeof(si);
2808 si.fMask = SIF_PAGE | SIF_RANGE;
2809 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2810 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2811 "Vertical scrollbar is invisible, should be visible.\n");
2812 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2813 "reported page/range is %d (%d..%d)\n",
2814 si.nPage, si.nMin, si.nMax);
2815
2816 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2817 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2818 memset(&si, 0, sizeof(si));
2819 si.cbSize = sizeof(si);
2820 si.fMask = SIF_PAGE | SIF_RANGE;
2821 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2822 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2823 "Vertical scrollbar is invisible, should be visible.\n");
2824 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2825 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2826 si.nPage, si.nMin, si.nMax);
2827
2828 /* Setting non-scrolling text again does *not* reset scrollbar range */
2829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2830 memset(&si, 0, sizeof(si));
2831 si.cbSize = sizeof(si);
2832 si.fMask = SIF_PAGE | SIF_RANGE;
2833 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2834 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2835 "Vertical scrollbar is invisible, should be visible.\n");
2836 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2837 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2838 si.nPage, si.nMin, si.nMax);
2839
2840 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2841 memset(&si, 0, sizeof(si));
2842 si.cbSize = sizeof(si);
2843 si.fMask = SIF_PAGE | SIF_RANGE;
2844 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2845 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2846 "Vertical scrollbar is invisible, should be visible.\n");
2847 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2848 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2849 si.nPage, si.nMin, si.nMax);
2850
2851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2852 memset(&si, 0, sizeof(si));
2853 si.cbSize = sizeof(si);
2854 si.fMask = SIF_PAGE | SIF_RANGE;
2855 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2856 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2857 "Vertical scrollbar is invisible, should be visible.\n");
2858 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2859 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2860 si.nPage, si.nMin, si.nMax);
2861
2862 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2863 memset(&si, 0, sizeof(si));
2864 si.cbSize = sizeof(si);
2865 si.fMask = SIF_PAGE | SIF_RANGE;
2866 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2868 "Vertical scrollbar is invisible, should be visible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2870 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2871 si.nPage, si.nMin, si.nMax);
2872
2873 DestroyWindow(hwndRichEdit);
2874
2875 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2876 hwndRichEdit = new_richedit(NULL);
2877
2878 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2879 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2885 "Vertical scrollbar is invisible, should be visible.\n");
2886 todo_wine {
2887 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2888 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2889 si.nPage, si.nMin, si.nMax);
2890 }
2891
2892 /* Ditto, see above */
2893 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2894 memset(&si, 0, sizeof(si));
2895 si.cbSize = sizeof(si);
2896 si.fMask = SIF_PAGE | SIF_RANGE;
2897 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2898 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2899 "Vertical scrollbar is invisible, should be visible.\n");
2900 todo_wine {
2901 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2902 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2903 si.nPage, si.nMin, si.nMax);
2904 }
2905
2906 /* Ditto, see above */
2907 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913 "Vertical scrollbar is invisible, should be visible.\n");
2914 todo_wine {
2915 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2916 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2917 si.nPage, si.nMin, si.nMax);
2918 }
2919
2920 /* Ditto, see above */
2921 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2922 memset(&si, 0, sizeof(si));
2923 si.cbSize = sizeof(si);
2924 si.fMask = SIF_PAGE | SIF_RANGE;
2925 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2926 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2927 "Vertical scrollbar is invisible, should be visible.\n");
2928 todo_wine {
2929 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2930 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2931 si.nPage, si.nMin, si.nMax);
2932 }
2933
2934 /* Ditto, see above */
2935 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2936 memset(&si, 0, sizeof(si));
2937 si.cbSize = sizeof(si);
2938 si.fMask = SIF_PAGE | SIF_RANGE;
2939 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2940 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2941 "Vertical scrollbar is invisible, should be visible.\n");
2942 todo_wine {
2943 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2944 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2945 si.nPage, si.nMin, si.nMax);
2946 }
2947
2948 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);