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 <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
34
35 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
36
37 #define ok_w3(format, szString1, szString2, szString3) \
38 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
39 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
41 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
42 format, string1, string2, string3);
43
44 static HMODULE hmoduleRichEdit;
45
46 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
47 HWND hwnd;
48 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
49 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
50 hmoduleRichEdit, NULL);
51 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
52 return hwnd;
53 }
54
55 static HWND new_richedit(HWND parent) {
56 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
57 }
58
59 static void processPendingMessages(void)
60 {
61 MSG msg;
62 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
63 TranslateMessage(&msg);
64 DispatchMessage(&msg);
65 }
66 }
67
68 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
69 {
70 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
71 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
72 SetFocus(hwnd);
73 keybd_event(mod_vk, mod_scan_code, 0, 0);
74 keybd_event(vk, scan_code, 0, 0);
75 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
76 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
77 processPendingMessages();
78 }
79
80 static void simulate_typing_characters(HWND hwnd, const char* szChars)
81 {
82 int ret;
83
84 while (*szChars != '\0') {
85 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
86 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
87 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
88 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
89 szChars++;
90 }
91 }
92
93 static const char haystack[] = "WINEWine wineWine wine WineWine";
94 /* ^0 ^10 ^20 ^30 */
95
96 struct find_s {
97 int start;
98 int end;
99 const char *needle;
100 int flags;
101 int expected_loc;
102 int _todo_wine;
103 };
104
105
106 struct find_s find_tests[] = {
107 /* Find in empty text */
108 {0, -1, "foo", FR_DOWN, -1, 0},
109 {0, -1, "foo", 0, -1, 0},
110 {0, -1, "", FR_DOWN, -1, 0},
111 {20, 5, "foo", FR_DOWN, -1, 0},
112 {5, 20, "foo", FR_DOWN, -1, 0}
113 };
114
115 struct find_s find_tests2[] = {
116 /* No-result find */
117 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
118 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
119
120 /* Subsequent finds */
121 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
122 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
123 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
124 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
125
126 /* Find backwards */
127 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
128 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
129 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
130
131 /* Case-insensitive */
132 {1, 31, "wInE", FR_DOWN, 4, 0},
133 {1, 31, "Wine", FR_DOWN, 4, 0},
134
135 /* High-to-low ranges */
136 {20, 5, "Wine", FR_DOWN, -1, 0},
137 {2, 1, "Wine", FR_DOWN, -1, 0},
138 {30, 29, "Wine", FR_DOWN, -1, 0},
139 {20, 5, "Wine", 0, 13, 0},
140
141 /* Find nothing */
142 {5, 10, "", FR_DOWN, -1, 0},
143 {10, 5, "", FR_DOWN, -1, 0},
144 {0, -1, "", FR_DOWN, -1, 0},
145 {10, 5, "", 0, -1, 0},
146
147 /* Whole-word search */
148 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
149 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
150 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
151 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
152 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
153 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
154 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
155
156 /* Bad ranges */
157 {5, 200, "XXX", FR_DOWN, -1, 0},
158 {-20, 20, "Wine", FR_DOWN, -1, 0},
159 {-20, 20, "Wine", FR_DOWN, -1, 0},
160 {-15, -20, "Wine", FR_DOWN, -1, 0},
161 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
162
163 /* Check the case noted in bug 4479 where matches at end aren't recognized */
164 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
165 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
166 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
167 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
168 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
169
170 /* The backwards case of bug 4479; bounds look right
171 * Fails because backward find is wrong */
172 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
173 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
174
175 {0, -1, "wineWine wine", 0, -1, 0},
176 };
177
178 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
179 int findloc;
180 FINDTEXT ft;
181 memset(&ft, 0, sizeof(ft));
182 ft.chrg.cpMin = f->start;
183 ft.chrg.cpMax = f->end;
184 ft.lpstrText = f->needle;
185 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
186 ok(findloc == f->expected_loc,
187 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
188 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
189 }
190
191 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
192 int id) {
193 int findloc;
194 FINDTEXTEX ft;
195 int expected_end_loc;
196
197 memset(&ft, 0, sizeof(ft));
198 ft.chrg.cpMin = f->start;
199 ft.chrg.cpMax = f->end;
200 ft.lpstrText = f->needle;
201 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
202 ok(findloc == f->expected_loc,
203 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
204 name, id, f->needle, f->start, f->end, f->flags, findloc);
205 ok(ft.chrgText.cpMin == f->expected_loc,
206 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
207 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
208 expected_end_loc = ((f->expected_loc == -1) ? -1
209 : f->expected_loc + strlen(f->needle));
210 ok(ft.chrgText.cpMax == expected_end_loc,
211 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
212 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
213 }
214
215 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
216 int num_tests)
217 {
218 int i;
219
220 for (i = 0; i < num_tests; i++) {
221 if (find[i]._todo_wine) {
222 todo_wine {
223 check_EM_FINDTEXT(hwnd, name, &find[i], i);
224 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
225 }
226 } else {
227 check_EM_FINDTEXT(hwnd, name, &find[i], i);
228 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
229 }
230 }
231 }
232
233 static void test_EM_FINDTEXT(void)
234 {
235 HWND hwndRichEdit = new_richedit(NULL);
236 CHARFORMAT2 cf2;
237
238 /* Empty rich edit control */
239 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
240 sizeof(find_tests)/sizeof(struct find_s));
241
242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
243
244 /* Haystack text */
245 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
246 sizeof(find_tests2)/sizeof(struct find_s));
247
248 /* Setting a format on an arbitrary range should have no effect in search
249 results. This tests correct offset reporting across runs. */
250 cf2.cbSize = sizeof(CHARFORMAT2);
251 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
252 (LPARAM) &cf2);
253 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
254 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
255 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
256 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
257
258 /* Haystack text, again */
259 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
260 sizeof(find_tests2)/sizeof(struct find_s));
261
262 /* Yet another range */
263 cf2.dwMask = CFM_BOLD | cf2.dwMask;
264 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
265 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
266 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
267
268 /* Haystack text, again */
269 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
270 sizeof(find_tests2)/sizeof(struct find_s));
271
272 DestroyWindow(hwndRichEdit);
273 }
274
275 static const struct getline_s {
276 int line;
277 size_t buffer_len;
278 const char *text;
279 } gl[] = {
280 {0, 10, "foo bar\r"},
281 {1, 10, "\r"},
282 {2, 10, "bar\r"},
283 {3, 10, "\r"},
284
285 /* Buffer smaller than line length */
286 {0, 2, "foo bar\r"},
287 {0, 1, "foo bar\r"},
288 {0, 0, "foo bar\r"}
289 };
290
291 static void test_EM_GETLINE(void)
292 {
293 int i;
294 HWND hwndRichEdit = new_richedit(NULL);
295 static const int nBuf = 1024;
296 char dest[1024], origdest[1024];
297 const char text[] = "foo bar\n"
298 "\n"
299 "bar\n";
300
301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
302
303 memset(origdest, 0xBB, nBuf);
304 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
305 {
306 int nCopied;
307 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
308 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
309 memset(dest, 0xBB, nBuf);
310 *(WORD *) dest = gl[i].buffer_len;
311
312 /* EM_GETLINE appends a "\r\0" to the end of the line
313 * nCopied counts up to and including the '\r' */
314 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
315 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
316 expected_nCopied);
317 /* two special cases since a parameter is passed via dest */
318 if (gl[i].buffer_len == 0)
319 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
320 "buffer_len=0\n");
321 else if (gl[i].buffer_len == 1)
322 ok(dest[0] == gl[i].text[0] && !dest[1] &&
323 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
324 else
325 {
326 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
327 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
328 ok(!strncmp(dest + expected_bytes_written, origdest
329 + expected_bytes_written, nBuf - expected_bytes_written),
330 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
331 }
332 }
333
334 DestroyWindow(hwndRichEdit);
335 }
336
337 static void test_EM_LINELENGTH(void)
338 {
339 HWND hwndRichEdit = new_richedit(NULL);
340 const char * text =
341 "richedit1\r"
342 "richedit1\n"
343 "richedit1\r\n"
344 "richedit1";
345 int offset_test[10][2] = {
346 {0, 9},
347 {5, 9},
348 {10, 9},
349 {15, 9},
350 {20, 9},
351 {25, 9},
352 {30, 9},
353 {35, 9},
354 {40, 0},
355 {45, 0},
356 };
357 int i;
358 LRESULT result;
359
360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
361
362 for (i = 0; i < 10; i++) {
363 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
364 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
365 offset_test[i][0], result, offset_test[i][1]);
366 }
367
368 DestroyWindow(hwndRichEdit);
369 }
370
371 static int get_scroll_pos_y(HWND hwnd)
372 {
373 POINT p = {-1, -1};
374 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
375 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
376 return p.y;
377 }
378
379 static void move_cursor(HWND hwnd, long charindex)
380 {
381 CHARRANGE cr;
382 cr.cpMax = charindex;
383 cr.cpMin = charindex;
384 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
385 }
386
387 static void line_scroll(HWND hwnd, int amount)
388 {
389 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
390 }
391
392 static void test_EM_SCROLLCARET(void)
393 {
394 int prevY, curY;
395 HWND hwndRichEdit = new_richedit(NULL);
396 const char text[] = "aa\n"
397 "this is a long line of text that should be longer than the "
398 "control's width\n"
399 "cc\n"
400 "dd\n"
401 "ee\n"
402 "ff\n"
403 "gg\n"
404 "hh\n";
405
406 /* Can't verify this */
407 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
408
409 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
410
411 /* Caret above visible window */
412 line_scroll(hwndRichEdit, 3);
413 prevY = get_scroll_pos_y(hwndRichEdit);
414 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
415 curY = get_scroll_pos_y(hwndRichEdit);
416 ok(prevY != curY, "%d == %d\n", prevY, curY);
417
418 /* Caret below visible window */
419 move_cursor(hwndRichEdit, sizeof(text) - 1);
420 line_scroll(hwndRichEdit, -3);
421 prevY = get_scroll_pos_y(hwndRichEdit);
422 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
423 curY = get_scroll_pos_y(hwndRichEdit);
424 ok(prevY != curY, "%d == %d\n", prevY, curY);
425
426 /* Caret in visible window */
427 move_cursor(hwndRichEdit, sizeof(text) - 2);
428 prevY = get_scroll_pos_y(hwndRichEdit);
429 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
430 curY = get_scroll_pos_y(hwndRichEdit);
431 ok(prevY == curY, "%d != %d\n", prevY, curY);
432
433 /* Caret still in visible window */
434 line_scroll(hwndRichEdit, -1);
435 prevY = get_scroll_pos_y(hwndRichEdit);
436 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
437 curY = get_scroll_pos_y(hwndRichEdit);
438 ok(prevY == curY, "%d != %d\n", prevY, curY);
439
440 DestroyWindow(hwndRichEdit);
441 }
442
443 static void test_EM_POSFROMCHAR(void)
444 {
445 HWND hwndRichEdit = new_richedit(NULL);
446 int i;
447 LRESULT result;
448 unsigned int height = 0;
449 int xpos = 0;
450 static const char text[] = "aa\n"
451 "this is a long line of text that should be longer than the "
452 "control's width\n"
453 "cc\n"
454 "dd\n"
455 "ee\n"
456 "ff\n"
457 "gg\n"
458 "hh\n";
459
460 /* Fill the control to lines to ensure that most of them are offscreen */
461 for (i = 0; i < 50; i++)
462 {
463 /* Do not modify the string; it is exactly 16 characters long. */
464 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
465 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
466 }
467
468 /*
469 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
470 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
471 Richedit 3.0 accepts either of the above API conventions.
472 */
473
474 /* Testing Richedit 2.0 API format */
475
476 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
477 Since all lines are identical and drawn with the same font,
478 they should have the same height... right?
479 */
480 for (i = 0; i < 50; i++)
481 {
482 /* All the lines are 16 characters long */
483 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
484 if (i == 0)
485 {
486 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
487 todo_wine {
488 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
489 }
490 xpos = LOWORD(result);
491 }
492 else if (i == 1)
493 {
494 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
495 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
496 height = HIWORD(result);
497 }
498 else
499 {
500 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
501 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
502 }
503 }
504
505 /* Testing position at end of text */
506 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
507 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
508 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
509
510 /* Testing position way past end of text */
511 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
512 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
513 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
514
515 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
516 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
517 for (i = 0; i < 50; i++)
518 {
519 /* All the lines are 16 characters long */
520 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
521 ok((signed short)(HIWORD(result)) == (i - 1) * height,
522 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
523 (signed short)(HIWORD(result)), (i - 1) * height);
524 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
525 }
526
527 /* Testing position at end of text */
528 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
529 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
530 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
531
532 /* Testing position way past end of text */
533 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
534 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
535 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
536
537 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
538 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
539 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
540
541 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
542 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
543 todo_wine {
544 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
545 }
546 xpos = LOWORD(result);
547
548 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
549 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
550 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
551 todo_wine {
552 /* Fails on builtin because horizontal scrollbar is not being shown */
553 ok((signed short)(LOWORD(result)) < xpos,
554 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
555 (signed short)(LOWORD(result)), xpos);
556 }
557 DestroyWindow(hwndRichEdit);
558 }
559
560 static void test_EM_SETCHARFORMAT(void)
561 {
562 HWND hwndRichEdit = new_richedit(NULL);
563 CHARFORMAT2 cf2;
564 int rc = 0;
565 int tested_effects[] = {
566 CFE_BOLD,
567 CFE_ITALIC,
568 CFE_UNDERLINE,
569 CFE_STRIKEOUT,
570 CFE_PROTECTED,
571 CFE_LINK,
572 CFE_SUBSCRIPT,
573 CFE_SUPERSCRIPT,
574 0
575 };
576 int i;
577 CHARRANGE cr;
578
579 /* Invalid flags, CHARFORMAT2 structure blanked out */
580 memset(&cf2, 0, sizeof(cf2));
581 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
582 (LPARAM) &cf2);
583 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
584
585 /* A valid flag, CHARFORMAT2 structure blanked out */
586 memset(&cf2, 0, sizeof(cf2));
587 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
588 (LPARAM) &cf2);
589 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
590
591 /* A valid flag, CHARFORMAT2 structure blanked out */
592 memset(&cf2, 0, sizeof(cf2));
593 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
594 (LPARAM) &cf2);
595 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
596
597 /* A valid flag, CHARFORMAT2 structure blanked out */
598 memset(&cf2, 0, sizeof(cf2));
599 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600 (LPARAM) &cf2);
601 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
602
603 /* A valid flag, CHARFORMAT2 structure blanked out */
604 memset(&cf2, 0, sizeof(cf2));
605 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
606 (LPARAM) &cf2);
607 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
608
609 /* Invalid flags, CHARFORMAT2 structure minimally filled */
610 memset(&cf2, 0, sizeof(cf2));
611 cf2.cbSize = sizeof(CHARFORMAT2);
612 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
613 (LPARAM) &cf2);
614 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
615 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
616 ok(rc == FALSE, "Should not be able to undo here.\n");
617 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
618
619 /* A valid flag, CHARFORMAT2 structure minimally filled */
620 memset(&cf2, 0, sizeof(cf2));
621 cf2.cbSize = sizeof(CHARFORMAT2);
622 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623 (LPARAM) &cf2);
624 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
625 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
626 ok(rc == FALSE, "Should not be able to undo here.\n");
627 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
628
629 /* A valid flag, CHARFORMAT2 structure minimally filled */
630 memset(&cf2, 0, sizeof(cf2));
631 cf2.cbSize = sizeof(CHARFORMAT2);
632 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
633 (LPARAM) &cf2);
634 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
635 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
636 ok(rc == FALSE, "Should not be able to undo here.\n");
637 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
638
639 /* A valid flag, CHARFORMAT2 structure minimally filled */
640 memset(&cf2, 0, sizeof(cf2));
641 cf2.cbSize = sizeof(CHARFORMAT2);
642 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
643 (LPARAM) &cf2);
644 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
645 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
646 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
647 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
648
649 /* A valid flag, CHARFORMAT2 structure minimally filled */
650 memset(&cf2, 0, sizeof(cf2));
651 cf2.cbSize = sizeof(CHARFORMAT2);
652 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
653 (LPARAM) &cf2);
654 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
656 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
657 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
658
659 cf2.cbSize = sizeof(CHARFORMAT2);
660 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
661 (LPARAM) &cf2);
662
663 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
664 cf2.cbSize = sizeof(CHARFORMAT2);
665 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
666 (LPARAM) &cf2);
667 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
668 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
669
670 /* wParam==0 is default char format, does not set modify */
671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673 ok(rc == 0, "Text marked as modified, expected not modified!\n");
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
675 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677 ok(rc == 0, "Text marked as modified, expected not modified!\n");
678
679 /* wParam==SCF_SELECTION sets modify if nonempty selection */
680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
681 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
682 ok(rc == 0, "Text marked as modified, expected not modified!\n");
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
684 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
685 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
686 ok(rc == 0, "Text marked as modified, expected not modified!\n");
687
688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
689 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690 ok(rc == 0, "Text marked as modified, expected not modified!\n");
691 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
692 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
693 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
694 ok(rc == 0, "Text marked as modified, expected not modified!\n");
695 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
697 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
700
701 /* wParam==SCF_ALL sets modify regardless of whether text is present */
702 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
703 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
704 ok(rc == 0, "Text marked as modified, expected not modified!\n");
705 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
706 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
707 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
708 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
709
710 DestroyWindow(hwndRichEdit);
711
712 /* EM_GETCHARFORMAT tests */
713 for (i = 0; tested_effects[i]; i++)
714 {
715 hwndRichEdit = new_richedit(NULL);
716 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
717
718 /* Need to set a TrueType font to get consistent CFM_BOLD results */
719 memset(&cf2, 0, sizeof(CHARFORMAT2));
720 cf2.cbSize = sizeof(CHARFORMAT2);
721 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
722 cf2.dwEffects = 0;
723 strcpy(cf2.szFaceName, "Courier New");
724 cf2.wWeight = FW_DONTCARE;
725 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
726
727 memset(&cf2, 0, sizeof(CHARFORMAT2));
728 cf2.cbSize = sizeof(CHARFORMAT2);
729 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
730 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
731 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
732 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
733 ||
734 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
735 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
736 ok((cf2.dwEffects & tested_effects[i]) == 0,
737 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
738
739 memset(&cf2, 0, sizeof(CHARFORMAT2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 cf2.dwMask = tested_effects[i];
742 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
743 cf2.dwMask = CFM_SUPERSCRIPT;
744 cf2.dwEffects = tested_effects[i];
745 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
746 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
747
748 memset(&cf2, 0, sizeof(CHARFORMAT2));
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
751 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
752 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
753 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
754 ||
755 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
756 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
757 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
758 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
759
760 memset(&cf2, 0, sizeof(CHARFORMAT2));
761 cf2.cbSize = sizeof(CHARFORMAT2);
762 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
764 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
765 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
766 ||
767 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
768 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
769 ok((cf2.dwEffects & tested_effects[i]) == 0,
770 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
771
772 memset(&cf2, 0, sizeof(CHARFORMAT2));
773 cf2.cbSize = sizeof(CHARFORMAT2);
774 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
775 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
776 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
777 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
778 ||
779 (cf2.dwMask & tested_effects[i]) == 0),
780 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
781
782 DestroyWindow(hwndRichEdit);
783 }
784
785 for (i = 0; tested_effects[i]; i++)
786 {
787 hwndRichEdit = new_richedit(NULL);
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789
790 /* Need to set a TrueType font to get consistent CFM_BOLD results */
791 memset(&cf2, 0, sizeof(CHARFORMAT2));
792 cf2.cbSize = sizeof(CHARFORMAT2);
793 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
794 cf2.dwEffects = 0;
795 strcpy(cf2.szFaceName, "Courier New");
796 cf2.wWeight = FW_DONTCARE;
797 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
798
799 memset(&cf2, 0, sizeof(CHARFORMAT2));
800 cf2.cbSize = sizeof(CHARFORMAT2);
801 cf2.dwMask = tested_effects[i];