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