Merge freeldr from amd64 branch:
[reactos.git] / rostests / winetests / riched20 / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
35
36 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
37
38 #define ok_w3(format, szString1, szString2, szString3) \
39 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
41 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
42 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
43 format, string1, string2, string3);
44
45 static HMODULE hmoduleRichEdit;
46
47 static int is_win9x = 0;
48
49 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
50 HWND hwnd;
51 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
55 return hwnd;
56 }
57
58 static HWND new_richedit(HWND parent) {
59 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
60 }
61
62 /* Keeps the window reponsive for the deley_time in seconds.
63 * This is useful for debugging a test to see what is happening. */
64 static void keep_responsive(time_t delay_time)
65 {
66 MSG msg;
67 time_t end;
68
69 /* The message pump uses PeekMessage() to empty the queue and then
70 * sleeps for 50ms before retrying the queue. */
71 end = time(NULL) + delay_time;
72 while (time(NULL) < end) {
73 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
74 TranslateMessage(&msg);
75 DispatchMessage(&msg);
76 } else {
77 Sleep(50);
78 }
79 }
80 }
81
82 static void processPendingMessages(void)
83 {
84 MSG msg;
85 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
86 TranslateMessage(&msg);
87 DispatchMessage(&msg);
88 }
89 }
90
91 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
92 {
93 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
94 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
95 SetFocus(hwnd);
96 keybd_event(mod_vk, mod_scan_code, 0, 0);
97 keybd_event(vk, scan_code, 0, 0);
98 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
99 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
100 processPendingMessages();
101 }
102
103 static void simulate_typing_characters(HWND hwnd, const char* szChars)
104 {
105 int ret;
106
107 while (*szChars != '\0') {
108 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
109 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
110 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
111 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
112 szChars++;
113 }
114 }
115
116 static BOOL hold_key(int vk)
117 {
118 BYTE key_state[256];
119 BOOL result;
120
121 result = GetKeyboardState(key_state);
122 ok(result, "GetKeyboardState failed.\n");
123 if (!result) return FALSE;
124 key_state[vk] |= 0x80;
125 result = SetKeyboardState(key_state);
126 ok(result, "SetKeyboardState failed.\n");
127 return result != 0;
128 }
129
130 static BOOL release_key(int vk)
131 {
132 BYTE key_state[256];
133 BOOL result;
134
135 result = GetKeyboardState(key_state);
136 ok(result, "GetKeyboardState failed.\n");
137 if (!result) return FALSE;
138 key_state[vk] &= ~0x80;
139 result = SetKeyboardState(key_state);
140 ok(result, "SetKeyboardState failed.\n");
141 return result != 0;
142 }
143
144 static const char haystack[] = "WINEWine wineWine wine WineWine";
145 /* ^0 ^10 ^20 ^30 */
146
147 struct find_s {
148 int start;
149 int end;
150 const char *needle;
151 int flags;
152 int expected_loc;
153 };
154
155
156 struct find_s find_tests[] = {
157 /* Find in empty text */
158 {0, -1, "foo", FR_DOWN, -1},
159 {0, -1, "foo", 0, -1},
160 {0, -1, "", FR_DOWN, -1},
161 {20, 5, "foo", FR_DOWN, -1},
162 {5, 20, "foo", FR_DOWN, -1}
163 };
164
165 struct find_s find_tests2[] = {
166 /* No-result find */
167 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
168 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
169
170 /* Subsequent finds */
171 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
172 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
173 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
174 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
175
176 /* Find backwards */
177 {19, 20, "Wine", FR_MATCHCASE, 13},
178 {10, 20, "Wine", FR_MATCHCASE, 4},
179 {20, 10, "Wine", FR_MATCHCASE, 13},
180
181 /* Case-insensitive */
182 {1, 31, "wInE", FR_DOWN, 4},
183 {1, 31, "Wine", FR_DOWN, 4},
184
185 /* High-to-low ranges */
186 {20, 5, "Wine", FR_DOWN, -1},
187 {2, 1, "Wine", FR_DOWN, -1},
188 {30, 29, "Wine", FR_DOWN, -1},
189 {20, 5, "Wine", 0, 13},
190
191 /* Find nothing */
192 {5, 10, "", FR_DOWN, -1},
193 {10, 5, "", FR_DOWN, -1},
194 {0, -1, "", FR_DOWN, -1},
195 {10, 5, "", 0, -1},
196
197 /* Whole-word search */
198 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
199 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
200 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
202 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
203 {11, -1, "winewine", FR_WHOLEWORD, 0},
204 {31, -1, "winewine", FR_WHOLEWORD, 23},
205
206 /* Bad ranges */
207 {5, 200, "XXX", FR_DOWN, -1},
208 {-20, 20, "Wine", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-15, -20, "Wine", FR_DOWN, -1},
211 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
212
213 /* Check the case noted in bug 4479 where matches at end aren't recognized */
214 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
215 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
216 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
218 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219
220 /* The backwards case of bug 4479; bounds look right
221 * Fails because backward find is wrong */
222 {19, 20, "WINE", FR_MATCHCASE, 0},
223 {0, 20, "WINE", FR_MATCHCASE, -1},
224
225 {0, -1, "wineWine wine", 0, -1},
226 };
227
228 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
229 int findloc;
230 FINDTEXT ft;
231 memset(&ft, 0, sizeof(ft));
232 ft.chrg.cpMin = f->start;
233 ft.chrg.cpMax = f->end;
234 ft.lpstrText = f->needle;
235 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
236 ok(findloc == f->expected_loc,
237 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
238 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
239 }
240
241 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
242 int id) {
243 int findloc;
244 FINDTEXTEX ft;
245 int expected_end_loc;
246
247 memset(&ft, 0, sizeof(ft));
248 ft.chrg.cpMin = f->start;
249 ft.chrg.cpMax = f->end;
250 ft.lpstrText = f->needle;
251 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
252 ok(findloc == f->expected_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, findloc);
255 ok(ft.chrgText.cpMin == f->expected_loc,
256 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
257 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
258 expected_end_loc = ((f->expected_loc == -1) ? -1
259 : f->expected_loc + strlen(f->needle));
260 ok(ft.chrgText.cpMax == expected_end_loc,
261 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
262 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
263 }
264
265 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
266 int num_tests)
267 {
268 int i;
269
270 for (i = 0; i < num_tests; i++) {
271 check_EM_FINDTEXT(hwnd, name, &find[i], i);
272 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
273 }
274 }
275
276 static void test_EM_FINDTEXT(void)
277 {
278 HWND hwndRichEdit = new_richedit(NULL);
279 CHARFORMAT2 cf2;
280
281 /* Empty rich edit control */
282 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
283 sizeof(find_tests)/sizeof(struct find_s));
284
285 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
286
287 /* Haystack text */
288 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
289 sizeof(find_tests2)/sizeof(struct find_s));
290
291 /* Setting a format on an arbitrary range should have no effect in search
292 results. This tests correct offset reporting across runs. */
293 cf2.cbSize = sizeof(CHARFORMAT2);
294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
295 (LPARAM) &cf2);
296 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
297 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
298 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
299 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
300
301 /* Haystack text, again */
302 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
303 sizeof(find_tests2)/sizeof(struct find_s));
304
305 /* Yet another range */
306 cf2.dwMask = CFM_BOLD | cf2.dwMask;
307 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
308 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
309 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
310
311 /* Haystack text, again */
312 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
313 sizeof(find_tests2)/sizeof(struct find_s));
314
315 DestroyWindow(hwndRichEdit);
316 }
317
318 static const struct getline_s {
319 int line;
320 size_t buffer_len;
321 const char *text;
322 } gl[] = {
323 {0, 10, "foo bar\r"},
324 {1, 10, "\r"},
325 {2, 10, "bar\r"},
326 {3, 10, "\r"},
327
328 /* Buffer smaller than line length */
329 {0, 2, "foo bar\r"},
330 {0, 1, "foo bar\r"},
331 {0, 0, "foo bar\r"}
332 };
333
334 static void test_EM_GETLINE(void)
335 {
336 int i;
337 HWND hwndRichEdit = new_richedit(NULL);
338 static const int nBuf = 1024;
339 char dest[1024], origdest[1024];
340 const char text[] = "foo bar\n"
341 "\n"
342 "bar\n";
343
344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
345
346 memset(origdest, 0xBB, nBuf);
347 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
348 {
349 int nCopied;
350 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
351 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
352 memset(dest, 0xBB, nBuf);
353 *(WORD *) dest = gl[i].buffer_len;
354
355 /* EM_GETLINE appends a "\r\0" to the end of the line
356 * nCopied counts up to and including the '\r' */
357 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
358 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
359 expected_nCopied);
360 /* two special cases since a parameter is passed via dest */
361 if (gl[i].buffer_len == 0)
362 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
363 "buffer_len=0\n");
364 else if (gl[i].buffer_len == 1)
365 ok(dest[0] == gl[i].text[0] && !dest[1] &&
366 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
367 else
368 {
369 /* Prepare hex strings of buffers to dump on failure. */
370 char expectedbuf[1024];
371 char resultbuf[1024];
372 int j;
373 resultbuf[0] = '\0';
374 for (j = 0; j < 32; j++)
375 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
376 expectedbuf[0] = '\0';
377 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
378 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
379 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
380 sprintf(expectedbuf+strlen(expectedbuf), "??");
381 for (; j < 32; j++) /* Bytes after declared buffer size */
382 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
383
384 /* Test the part of the buffer that is expected to be written according
385 * to the MSDN documentation fo EM_GETLINE, which does not state that
386 * a NULL terminating character will be added unless no text is copied.
387 *
388 * Windows 95, 98 & NT do not append a NULL terminating character, but
389 * Windows 2000 and up do append a NULL terminating character if there
390 * is space in the buffer. The test will ignore this difference. */
391 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
392 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
393 i, expected_bytes_written, expectedbuf, resultbuf);
394 /* Test the part of the buffer after the declared length to make sure
395 * there are no buffer overruns. */
396 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
397 nBuf - gl[i].buffer_len),
398 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
399 i, expected_bytes_written, expectedbuf, resultbuf);
400 }
401 }
402
403 DestroyWindow(hwndRichEdit);
404 }
405
406 static void test_EM_LINELENGTH(void)
407 {
408 HWND hwndRichEdit = new_richedit(NULL);
409 const char * text =
410 "richedit1\r"
411 "richedit1\n"
412 "richedit1\r\n"
413 "richedit1";
414 int offset_test[10][2] = {
415 {0, 9},
416 {5, 9},
417 {10, 9},
418 {15, 9},
419 {20, 9},
420 {25, 9},
421 {30, 9},
422 {35, 9},
423 {40, 0},
424 {45, 0},
425 };
426 int i;
427 LRESULT result;
428
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
430
431 for (i = 0; i < 10; i++) {
432 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
433 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
434 offset_test[i][0], result, offset_test[i][1]);
435 }
436
437 DestroyWindow(hwndRichEdit);
438 }
439
440 static int get_scroll_pos_y(HWND hwnd)
441 {
442 POINT p = {-1, -1};
443 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
444 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
445 return p.y;
446 }
447
448 static void move_cursor(HWND hwnd, long charindex)
449 {
450 CHARRANGE cr;
451 cr.cpMax = charindex;
452 cr.cpMin = charindex;
453 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
454 }
455
456 static void line_scroll(HWND hwnd, int amount)
457 {
458 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
459 }
460
461 static void test_EM_SCROLLCARET(void)
462 {
463 int prevY, curY;
464 const char text[] = "aa\n"
465 "this is a long line of text that should be longer than the "
466 "control's width\n"
467 "cc\n"
468 "dd\n"
469 "ee\n"
470 "ff\n"
471 "gg\n"
472 "hh\n";
473 /* The richedit window height needs to be large enough vertically to fit in
474 * more than two lines of text, so the new_richedit function can't be used
475 * since a height of 60 was not large enough on some systems.
476 */
477 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
478 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
479 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
480 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
481
482 /* Can't verify this */
483 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
484
485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
486
487 /* Caret above visible window */
488 line_scroll(hwndRichEdit, 3);
489 prevY = get_scroll_pos_y(hwndRichEdit);
490 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 curY = get_scroll_pos_y(hwndRichEdit);
492 ok(prevY != curY, "%d == %d\n", prevY, curY);
493
494 /* Caret below visible window */
495 move_cursor(hwndRichEdit, sizeof(text) - 1);
496 line_scroll(hwndRichEdit, -3);
497 prevY = get_scroll_pos_y(hwndRichEdit);
498 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
499 curY = get_scroll_pos_y(hwndRichEdit);
500 ok(prevY != curY, "%d == %d\n", prevY, curY);
501
502 /* Caret in visible window */
503 move_cursor(hwndRichEdit, sizeof(text) - 2);
504 prevY = get_scroll_pos_y(hwndRichEdit);
505 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
506 curY = get_scroll_pos_y(hwndRichEdit);
507 ok(prevY == curY, "%d != %d\n", prevY, curY);
508
509 /* Caret still in visible window */
510 line_scroll(hwndRichEdit, -1);
511 prevY = get_scroll_pos_y(hwndRichEdit);
512 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
513 curY = get_scroll_pos_y(hwndRichEdit);
514 ok(prevY == curY, "%d != %d\n", prevY, curY);
515
516 DestroyWindow(hwndRichEdit);
517 }
518
519 static void test_EM_POSFROMCHAR(void)
520 {
521 HWND hwndRichEdit = new_richedit(NULL);
522 int i;
523 LRESULT result;
524 unsigned int height = 0;
525 int xpos = 0;
526 POINTL pt;
527 static const char text[] = "aa\n"
528 "this is a long line of text that should be longer than the "
529 "control's width\n"
530 "cc\n"
531 "dd\n"
532 "ee\n"
533 "ff\n"
534 "gg\n"
535 "hh\n";
536
537 /* Fill the control to lines to ensure that most of them are offscreen */
538 for (i = 0; i < 50; i++)
539 {
540 /* Do not modify the string; it is exactly 16 characters long. */
541 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
542 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
543 }
544
545 /*
546 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
547 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
548 Richedit 3.0 accepts either of the above API conventions.
549 */
550
551 /* Testing Richedit 2.0 API format */
552
553 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
554 Since all lines are identical and drawn with the same font,
555 they should have the same height... right?
556 */
557 for (i = 0; i < 50; i++)
558 {
559 /* All the lines are 16 characters long */
560 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
561 if (i == 0)
562 {
563 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
564 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
565 xpos = LOWORD(result);
566 }
567 else if (i == 1)
568 {
569 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
570 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 height = HIWORD(result);
572 }
573 else
574 {
575 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
576 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
577 }
578 }
579
580 /* Testing position at end of text */
581 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
582 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
583 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
584
585 /* Testing position way past end of text */
586 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
587 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
589
590 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
591 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
592 for (i = 0; i < 50; i++)
593 {
594 /* All the lines are 16 characters long */
595 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
596 ok((signed short)(HIWORD(result)) == (i - 1) * height,
597 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
598 (signed short)(HIWORD(result)), (i - 1) * height);
599 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
600 }
601
602 /* Testing position at end of text */
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
604 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
606
607 /* Testing position way past end of text */
608 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
609 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
610 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
611
612 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
614 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
615
616 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
617 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
618 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
619 xpos = LOWORD(result);
620
621 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
623 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
624 ok((signed short)(LOWORD(result)) < xpos,
625 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
626 (signed short)(LOWORD(result)), xpos);
627 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
628
629 /* Test around end of text that doesn't end in a newline. */
630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
631 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
632 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
633 ok(pt.x > 1, "pt.x = %d\n", pt.x);
634 xpos = pt.x;
635 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
636 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
637 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
638 xpos = pt.x;
639 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
640 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
641 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
642
643 /* Try a negative position. */
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
645 ok(pt.x == 1, "pt.x = %d\n", pt.x);
646
647 DestroyWindow(hwndRichEdit);
648 }
649
650 static void test_EM_SETCHARFORMAT(void)
651 {
652 HWND hwndRichEdit = new_richedit(NULL);
653 CHARFORMAT2 cf2;
654 int rc = 0;
655 int tested_effects[] = {
656 CFE_BOLD,
657 CFE_ITALIC,
658 CFE_UNDERLINE,
659 CFE_STRIKEOUT,
660 CFE_PROTECTED,
661 CFE_LINK,
662 CFE_SUBSCRIPT,
663 CFE_SUPERSCRIPT,
664 0
665 };
666 int i;
667 CHARRANGE cr;
668
669 /* Invalid flags, CHARFORMAT2 structure blanked out */
670 memset(&cf2, 0, sizeof(cf2));
671 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
672 (LPARAM) &cf2);
673 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
674
675 /* A valid flag, CHARFORMAT2 structure blanked out */
676 memset(&cf2, 0, sizeof(cf2));
677 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
678 (LPARAM) &cf2);
679 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
680
681 /* A valid flag, CHARFORMAT2 structure blanked out */
682 memset(&cf2, 0, sizeof(cf2));
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
684 (LPARAM) &cf2);
685 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
686
687 /* A valid flag, CHARFORMAT2 structure blanked out */
688 memset(&cf2, 0, sizeof(cf2));
689 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
690 (LPARAM) &cf2);
691 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
692
693 /* A valid flag, CHARFORMAT2 structure blanked out */
694 memset(&cf2, 0, sizeof(cf2));
695 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
696 (LPARAM) &cf2);
697 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
698
699 /* Invalid flags, CHARFORMAT2 structure minimally filled */
700 memset(&cf2, 0, sizeof(cf2));
701 cf2.cbSize = sizeof(CHARFORMAT2);
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
703 (LPARAM) &cf2);
704 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
705 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
706 ok(rc == FALSE, "Should not be able to undo here.\n");
707 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
708
709 /* A valid flag, CHARFORMAT2 structure minimally filled */
710 memset(&cf2, 0, sizeof(cf2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
713 (LPARAM) &cf2);
714 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
715 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
716 ok(rc == FALSE, "Should not be able to undo here.\n");
717 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
718
719 /* A valid flag, CHARFORMAT2 structure minimally filled */
720 memset(&cf2, 0, sizeof(cf2));
721 cf2.cbSize = sizeof(CHARFORMAT2);
722 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
723 (LPARAM) &cf2);
724 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
725 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
726 ok(rc == FALSE, "Should not be able to undo here.\n");
727 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
728
729 /* A valid flag, CHARFORMAT2 structure minimally filled */
730 memset(&cf2, 0, sizeof(cf2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
733 (LPARAM) &cf2);
734 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
735 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
736 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
737 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
738
739 /* A valid flag, CHARFORMAT2 structure minimally filled */
740 memset(&cf2, 0, sizeof(cf2));
741 cf2.cbSize = sizeof(CHARFORMAT2);
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
743 (LPARAM) &cf2);
744 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
745 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
746 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
747 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
748
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
751 (LPARAM) &cf2);
752
753 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
756 (LPARAM) &cf2);
757 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
758 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
759
760 /* wParam==0 is default char format, does not set modify */
761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
762 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
763 ok(rc == 0, "Text marked as modified, expected not modified!\n");
764 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
765 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
766 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
767 ok(rc == 0, "Text marked as modified, expected not modified!\n");
768
769 /* wParam==SCF_SELECTION sets modify if nonempty selection */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
777
778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
779 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
780 ok(rc == 0, "Text marked as modified, expected not modified!\n");
781 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
784 ok(rc == 0, "Text marked as modified, expected not modified!\n");
785 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
786 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
790
791 /* wParam==SCF_ALL sets modify regardless of whether text is present */
792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
799
800 DestroyWindow(hwndRichEdit);
801
802 /* EM_GETCHARFORMAT tests */
803 for (i = 0; tested_effects[i]; i++)
804 {
805 hwndRichEdit = new_richedit(NULL);
806 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
807
808 /* Need to set a TrueType font to get consistent CFM_BOLD results */
809 memset(&cf2, 0, sizeof(CHARFORMAT2));
810 cf2.cbSize = sizeof(CHARFORMAT2);
811 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
812 cf2.dwEffects = 0;
813 strcpy(cf2.szFaceName, "Courier New");
814 cf2.wWeight = FW_DONTCARE;
815 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
816
817 memset(&cf2, 0, sizeof(CHARFORMAT2));
818 cf2.cbSize = sizeof(CHARFORMAT2);
819 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
821 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
822 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
823 ||
824 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
826 ok((cf2.dwEffects & tested_effects[i]) == 0,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
828
829 memset(&cf2, 0, sizeof(CHARFORMAT2));
830 cf2.cbSize = sizeof(CHARFORMAT2);
831 cf2.dwMask = tested_effects[i];
832 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
833 cf2.dwMask = CFM_SUPERSCRIPT;
834 cf2.dwEffects = tested_effects[i];
835 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
836 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
837
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
843 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
844 ||
845 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
846 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
847 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
848 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
849
850 memset(&cf2, 0, sizeof(CHARFORMAT2));
851 cf2.cbSize = sizeof(CHARFORMAT2);
852 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
853 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
854 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
855 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
856 ||
857 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
858 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
859 ok((cf2.dwEffects & tested_effects[i]) == 0,
860 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
861
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
867 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
868 ||
869 (cf2.dwMask & tested_effects[i]) == 0),
870 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
871
872 DestroyWindow(hwndRichEdit);
873 }
874
875 for (i = 0; tested_effects[i]; i++)
876 {
877 hwndRichEdit = new_richedit(NULL);
878 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
879
880 /* Need to set a TrueType font to get consistent CFM_BOLD results */
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
884 cf2.dwEffects = 0;
885 strcpy(cf2.szFaceName, "Courier New");
886 cf2.wWeight = FW_DONTCARE;
887 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
888
889 memset(&cf2, 0, sizeof(CHARFORMAT2));
890 cf2.cbSize = sizeof(CHARFORMAT2);
891 cf2.dwMask = tested_effects[i];
892 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
893 cf2.dwMask = CFM_SUPERSCRIPT;
894 cf2.dwEffects = tested_effects[i];
895 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
897
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
901 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
902 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
903 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
904 ||
905 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
906 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
907 ok((cf2.dwEffects & tested_effects[i]) == 0,
908 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
909
910 memset(&cf2, 0, sizeof(CHARFORMAT2));
911 cf2.cbSize = sizeof(CHARFORMAT2);
912 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
913 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
914 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
915 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
916 ||
917 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
918 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
919 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
920 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
921
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
925 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
926 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
927 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
928 ||
929 (cf2.dwMask & tested_effects[i]) == 0),
930 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
932 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
933
934 DestroyWindow(hwndRichEdit);
935 }
936
937 /* Effects applied on an empty selection should take effect when selection is
938 replaced with text */
939 hwndRichEdit = new_richedit(NULL);
940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
941 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
942
943 memset(&cf2, 0, sizeof(CHARFORMAT2));
944 cf2.cbSize = sizeof(CHARFORMAT2);
945 cf2.dwMask = CFM_BOLD;
946 cf2.dwEffects = CFE_BOLD;
947 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
948
949 /* Selection is now nonempty */
950 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
951
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
955 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
956
957 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
958 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
959 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
960 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
961
962
963 /* Set two effects on an empty selection */
964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
966
967 memset(&cf2, 0, sizeof(CHARFORMAT2));
968 cf2.cbSize = sizeof(CHARFORMAT2);
969 cf2.dwMask = CFM_BOLD;
970 cf2.dwEffects = CFE_BOLD;
971 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 cf2.dwMask = CFM_ITALIC;
973 cf2.dwEffects = CFE_ITALIC;
974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
975
976 /* Selection is now nonempty */
977 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
978
979 memset(&cf2, 0, sizeof(CHARFORMAT2));
980 cf2.cbSize = sizeof(CHARFORMAT2);
981 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
982 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
983
984 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
985 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
986 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
987 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
988
989 /* Setting the (empty) selection to exactly the same place as before should
990 NOT clear the insertion style! */
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
992 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
993
994 memset(&cf2, 0, sizeof(CHARFORMAT2));
995 cf2.cbSize = sizeof(CHARFORMAT2);
996 cf2.dwMask = CFM_BOLD;
997 cf2.dwEffects = CFE_BOLD;
998 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
999
1000 /* Empty selection in same place, insert style should NOT be forgotten here. */
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1002
1003 /* Selection is now nonempty */
1004 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1005
1006 memset(&cf2, 0, sizeof(CHARFORMAT2));
1007 cf2.cbSize = sizeof(CHARFORMAT2);
1008 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1009 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1010
1011 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1012 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1013 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1014 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1015
1016 /* Ditto with EM_EXSETSEL */
1017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1018 cr.cpMin = 2; cr.cpMax = 2;
1019 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1020
1021 memset(&cf2, 0, sizeof(CHARFORMAT2));
1022 cf2.cbSize = sizeof(CHARFORMAT2);
1023 cf2.dwMask = CFM_BOLD;
1024 cf2.dwEffects = CFE_BOLD;
1025 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1026
1027 /* Empty selection in same place, insert style should NOT be forgotten here. */
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1030
1031 /* Selection is now nonempty */
1032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1033
1034 memset(&cf2, 0, sizeof(CHARFORMAT2));
1035 cf2.cbSize = sizeof(CHARFORMAT2);
1036 cr.cpMin = 2; cr.cpMax = 6;
1037 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1038 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1039
1040 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1041 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1042 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1043 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1044
1045 DestroyWindow(hwndRichEdit);
1046 }
1047
1048 static void test_EM_SETTEXTMODE(void)
1049 {
1050 HWND hwndRichEdit = new_richedit(NULL);
1051 CHARFORMAT2 cf2, cf2test;
1052 CHARRANGE cr;
1053 int rc = 0;
1054
1055 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1056 /*Insert text into the control*/
1057
1058 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1059
1060 /*Attempt to change the control to plain text mode*/
1061 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1062 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1063
1064 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1065 If rich text is pasted, it should have the same formatting as the rest
1066 of the text in the control*/
1067
1068 /*Italicize the text
1069 *NOTE: If the default text was already italicized, the test will simply
1070 reverse; in other words, it will copy a regular "wine" into a plain
1071 text window that uses an italicized format*/
1072 cf2.cbSize = sizeof(CHARFORMAT2);
1073 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1074 (LPARAM) &cf2);
1075
1076 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1077 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1078
1079 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1080 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1081
1082 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1083 however, SCF_ALL has been implemented*/
1084 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1085 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1086
1087 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1088 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1089
1090 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1091
1092 /*Select the string "wine"*/
1093 cr.cpMin = 0;
1094 cr.cpMax = 4;
1095 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1096
1097 /*Copy the italicized "wine" to the clipboard*/
1098 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1099
1100 /*Reset the formatting to default*/
1101 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1102 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1103 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1104
1105 /*Clear the text in the control*/
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1107
1108 /*Switch to Plain Text Mode*/
1109 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1110 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1111
1112 /*Input "wine" again in normal format*/
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1114
1115 /*Paste the italicized "wine" into the control*/
1116 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1117
1118 /*Select a character from the first "wine" string*/
1119 cr.cpMin = 2;
1120 cr.cpMax = 3;
1121 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1122
1123 /*Retrieve its formatting*/
1124 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1125 (LPARAM) &cf2);
1126
1127 /*Select a character from the second "wine" string*/
1128 cr.cpMin = 5;
1129 cr.cpMax = 6;
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1131
1132 /*Retrieve its formatting*/
1133 cf2test.cbSize = sizeof(CHARFORMAT2);
1134 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1135 (LPARAM) &cf2test);
1136
1137 /*Compare the two formattings*/
1138 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1139 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1140 cf2.dwEffects, cf2test.dwEffects);
1141 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1142 printing "wine" in the current format(normal)
1143 pasting "wine" from the clipboard(italicized)
1144 comparing the two formats(should differ)*/
1145
1146 /*Attempt to switch with text in control*/
1147 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1148 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1149
1150 /*Clear control*/
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1152
1153 /*Switch into Rich Text mode*/
1154 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1155 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1156
1157 /*Print "wine" in normal formatting into the control*/
1158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1159
1160 /*Paste italicized "wine" into the control*/
1161 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1162
1163 /*Select text from the first "wine" string*/
1164 cr.cpMin = 1;
1165 cr.cpMax = 3;
1166 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1167
1168 /*Retrieve its formatting*/
1169 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1170 (LPARAM) &cf2);
1171
1172 /*Select text from the second "wine" string*/
1173 cr.cpMin = 6;
1174 cr.cpMax = 7;
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1176
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1179 (LPARAM) &cf2test);
1180
1181 /*Test that the two formattings are not the same*/
1182 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1183 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1184 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1185
1186 DestroyWindow(hwndRichEdit);
1187 }
1188
1189 static void test_SETPARAFORMAT(void)
1190 {
1191 HWND hwndRichEdit = new_richedit(NULL);
1192 PARAFORMAT2 fmt;
1193 HRESULT ret;
1194 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1195 fmt.cbSize = sizeof(PARAFORMAT2);
1196 fmt.dwMask = PFM_ALIGNMENT;
1197 fmt.wAlignment = PFA_LEFT;
1198
1199 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1200 ok(ret != 0, "expected non-zero got %d\n", ret);
1201
1202 fmt.cbSize = sizeof(PARAFORMAT2);
1203 fmt.dwMask = -1;
1204 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1205 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1206 * between richedit different native builds of riched20.dll
1207 * used on different Windows versions. */
1208 ret &= ~PFM_TABLEROWDELIMITER;
1209 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1210
1211 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1212 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1213
1214 DestroyWindow(hwndRichEdit);
1215 }
1216
1217 static void test_TM_PLAINTEXT(void)
1218 {
1219 /*Tests plain text properties*/
1220
1221 HWND hwndRichEdit = new_richedit(NULL);
1222 CHARFORMAT2 cf2, cf2test;
1223 CHARRANGE cr;
1224 int rc = 0;
1225
1226 /*Switch to plain text mode*/
1227
1228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1230
1231 /*Fill control with text*/
1232
1233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1234
1235 /*Select some text and bold it*/
1236
1237 cr.cpMin = 10;
1238 cr.cpMax = 20;
1239 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1240 cf2.cbSize = sizeof(CHARFORMAT2);
1241 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1242 (LPARAM) &cf2);
1243
1244 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1245 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1246
1247 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1248 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1249
1250 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1251 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1252
1253 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1254 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1255
1256 /*Get the formatting of those characters*/
1257
1258 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1259
1260 /*Get the formatting of some other characters*/
1261 cf2test.cbSize = sizeof(CHARFORMAT2);
1262 cr.cpMin = 21;
1263 cr.cpMax = 30;
1264 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1265 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1266
1267 /*Test that they are the same as plain text allows only one formatting*/
1268
1269 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1270 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1271 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1272
1273 /*Fill the control with a "wine" string, which when inserted will be bold*/
1274
1275 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1276
1277 /*Copy the bolded "wine" string*/
1278
1279 cr.cpMin = 0;
1280 cr.cpMax = 4;
1281 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1282 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1283
1284 /*Swap back to rich text*/
1285
1286 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1287 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1288
1289 /*Set the default formatting to bold italics*/
1290
1291 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1292 cf2.dwMask |= CFM_ITALIC;
1293 cf2.dwEffects ^= CFE_ITALIC;
1294 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1295 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1296
1297 /*Set the text in the control to "wine", which will be bold and italicized*/
1298
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1300
1301 /*Paste the plain text "wine" string, which should take the insert
1302 formatting, which at the moment is bold italics*/
1303
1304 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1305
1306 /*Select the first "wine" string and retrieve its formatting*/
1307
1308 cr.cpMin = 1;
1309 cr.cpMax = 3;
1310 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1311 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1312
1313 /*Select the second "wine" string and retrieve its formatting*/
1314
1315 cr.cpMin = 5;
1316 cr.cpMax = 7;
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1319
1320 /*Compare the two formattings. They should be the same.*/
1321
1322 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1323 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1324 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1325 DestroyWindow(hwndRichEdit);
1326 }
1327
1328 static void test_WM_GETTEXT(void)
1329 {
1330 HWND hwndRichEdit = new_richedit(NULL);
1331 static const char text[] = "Hello. My name is RichEdit!";
1332 static const char text2[] = "Hello. My name is RichEdit!\r";
1333 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1334 char buffer[1024] = {0};
1335 int result;
1336
1337 /* Baseline test with normal-sized buffer */
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1339 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(result == lstrlen(buffer),
1341 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1342 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1343 result = strcmp(buffer,text);
1344 ok(result == 0,
1345 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1346
1347 /* Test for returned value of WM_GETTEXTLENGTH */
1348 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1349 ok(result == lstrlen(text),
1350 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1351 result, lstrlen(text));
1352
1353 /* Test for behavior in overflow case */
1354 memset(buffer, 0, 1024);
1355 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1356 ok(result == 0 ||
1357 result == lstrlenA(text) - 1, /* XP, win2k3 */
1358 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1359 result = strcmp(buffer,text);
1360 if (result)
1361 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1362 ok(result == 0,
1363 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1364
1365 /* Baseline test with normal-sized buffer and carriage return */
1366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1367 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1368 ok(result == lstrlen(buffer),
1369 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1370 result = strcmp(buffer,text2_after);
1371 ok(result == 0,
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1373
1374 /* Test for returned value of WM_GETTEXTLENGTH */
1375 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1376 ok(result == lstrlen(text2_after),
1377 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1378 result, lstrlen(text2_after));
1379
1380 /* Test for behavior of CRLF conversion in case of overflow */
1381 memset(buffer, 0, 1024);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1383 ok(result == 0 ||
1384 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1385 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1386 result = strcmp(buffer,text2);
1387 if (result)
1388 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1389 ok(result == 0,
1390 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1391
1392 DestroyWindow(hwndRichEdit);
1393 }
1394
1395 static void test_EM_GETTEXTRANGE(void)
1396 {
1397 HWND hwndRichEdit = new_richedit(NULL);
1398 const char * text1 = "foo bar\r\nfoo bar";
1399 const char * text2 = "foo bar\rfoo bar";
1400 const char * expect = "bar\rfoo";
1401 char buffer[1024] = {0};
1402 LRESULT result;
1403 TEXTRANGEA textRange;
1404
1405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1406
1407 textRange.lpstrText = buffer;
1408 textRange.chrg.cpMin = 4;
1409 textRange.chrg.cpMax = 11;
1410 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1411 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1412 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1413
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1415
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1422
1423 DestroyWindow(hwndRichEdit);
1424 }
1425
1426 static void test_EM_GETSELTEXT(void)
1427 {
1428 HWND hwndRichEdit = new_richedit(NULL);
1429 const char * text1 = "foo bar\r\nfoo bar";
1430 const char * text2 = "foo bar\rfoo bar";
1431 const char * expect = "bar\rfoo";
1432 char buffer[1024] = {0};
1433 LRESULT result;
1434
1435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1436
1437 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1438 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1439 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1440 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1441
1442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1443
1444 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1445 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1446 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1447 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1448
1449 DestroyWindow(hwndRichEdit);
1450 }
1451
1452 /* FIXME: need to test unimplemented options and robustly test wparam */
1453 static void test_EM_SETOPTIONS(void)
1454 {
1455 HWND hwndRichEdit;
1456 static const char text[] = "Hello. My name is RichEdit!";
1457 char buffer[1024] = {0};
1458 DWORD dwStyle, options, oldOptions;
1459 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1460 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1461 ES_SELECTIONBAR|ES_VERTICAL;
1462
1463 /* Test initial options. */
1464 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1465 0, 0, 200, 60, NULL, NULL,
1466 hmoduleRichEdit, NULL);
1467 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1468 RICHEDIT_CLASS, (int) GetLastError());
1469 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1470 ok(options == 0, "Incorrect initial options %x\n", options);
1471 DestroyWindow(hwndRichEdit);
1472
1473 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1474 WS_POPUP|WS_HSCROLL|WS_VSCROLL,
1475 0, 0, 200, 60, NULL, NULL,
1476 hmoduleRichEdit, NULL);
1477 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1478 RICHEDIT_CLASS, (int) GetLastError());
1479 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1480 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1481 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1482 "Incorrect initial options %x\n", options);
1483
1484 /* NEGATIVE TESTING - NO OPTIONS SET */
1485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1486 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1487
1488 /* testing no readonly by sending 'a' to the control*/
1489 SetFocus(hwndRichEdit);
1490 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1491 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1492 ok(buffer[0]=='a',
1493 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1495
1496 /* READONLY - sending 'a' to the control */
1497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1498 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1499 SetFocus(hwndRichEdit);
1500 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1501 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1502 ok(buffer[0]==text[0],
1503 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1504
1505 /* EM_SETOPTIONS changes the window style, but changing the
1506 * window style does not change the options. */
1507 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1508 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1509 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1510 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1511 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1512 /* Confirm that the text is still read only. */
1513 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1514 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1515 ok(buffer[0]==text[0],
1516 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1517
1518 oldOptions = options;
1519 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1520 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1521 ok(options == oldOptions,
1522 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1523
1524 DestroyWindow(hwndRichEdit);
1525 }
1526
1527 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1528 {
1529 CHARFORMAT2W text_format;
1530 text_format.cbSize = sizeof(text_format);
1531 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1532 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1533 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1534 }
1535
1536 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1537 {
1538 int link_present = 0;
1539
1540 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1541 if (is_url)
1542 { /* control text is url; should get CFE_LINK */
1543 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1544 }
1545 else
1546 {
1547 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1548 }
1549 }
1550
1551 static HWND new_static_wnd(HWND parent) {
1552 return new_window("Static", 0, parent);
1553 }
1554
1555 static void test_EM_AUTOURLDETECT(void)
1556 {
1557 /* DO NOT change the properties of the first two elements. To shorten the
1558 tests, all tests after WM_SETTEXT test just the first two elements -
1559 one non-URL and one URL */
1560 struct urls_s {
1561 const char *text;
1562 int is_url;
1563 } urls[12] = {
1564 {"winehq.org", 0},
1565 {"http://www.winehq.org", 1},
1566 {"http//winehq.org", 0},
1567 {"ww.winehq.org", 0},
1568 {"www.winehq.org", 1},
1569 {"ftp://192.168.1.1", 1},
1570 {"ftp//192.168.1.1", 0},
1571 {"mailto:your@email.com", 1},
1572 {"prospero:prosperoserver", 1},
1573 {"telnet:test", 1},
1574 {"news:newserver", 1},
1575 {"wais:waisserver", 1}
1576 };
1577
1578 int i, j;
1579 int urlRet=-1;
1580 HWND hwndRichEdit, parent;
1581
1582 /* All of the following should cause the URL to be detected */
1583 const char * templates_delim[] = {
1584 "This is some text with X on it",
1585 "This is some text with (X) on it",
1586 "This is some text with X\r on it",
1587 "This is some text with ---X--- on it",
1588 "This is some text with \"X\" on it",
1589 "This is some text with 'X' on it",
1590 "This is some text with 'X' on it",
1591 "This is some text with :X: on it",
1592
1593 "This text ends with X",
1594
1595 "This is some text with X) on it",
1596 "This is some text with X--- on it",
1597 "This is some text with X\" on it",
1598 "This is some text with X' on it",
1599 "This is some text with X: on it",
1600
1601 "This is some text with (X on it",
1602 "This is some text with \rX on it",
1603 "This is some text with ---X on it",
1604 "This is some text with \"X on it",
1605 "This is some text with 'X on it",
1606 "This is some text with :X on it",
1607 };
1608 /* None of these should cause the URL to be detected */
1609 const char * templates_non_delim[] = {
1610 "This is some text with |X| on it",
1611 "This is some text with *X* on it",
1612 "This is some text with /X/ on it",
1613 "This is some text with +X+ on it",
1614 "This is some text with %X% on it",
1615 "This is some text with #X# on it",
1616 "This is some text with @X@ on it",
1617 "This is some text with \\X\\ on it",
1618 "This is some text with |X on it",
1619 "This is some text with *X on it",
1620 "This is some text with /X on it",
1621 "This is some text with +X on it",
1622 "This is some text with %X on it",
1623 "This is some text with #X on it",
1624 "This is some text with @X on it",
1625 "This is some text with \\X on it",
1626 };
1627 /* All of these cause the URL detection to be extended by one more byte,
1628 thus demonstrating that the tested character is considered as part
1629 of the URL. */
1630 const char * templates_xten_delim[] = {
1631 "This is some text with X| on it",
1632 "This is some text with X* on it",
1633 "This is some text with X/ on it",
1634 "This is some text with X+ on it",
1635 "This is some text with X% on it",
1636 "This is some text with X# on it",
1637 "This is some text with X@ on it",
1638 "This is some text with X\\ on it",
1639 };
1640 char buffer[1024];
1641
1642 parent = new_static_wnd(NULL);
1643 hwndRichEdit = new_richedit(parent);
1644 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1645 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1646 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1647 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1648 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1649 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1650 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1651 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1652 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1653 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1654 /* for each url, check the text to see if CFE_LINK effect is present */
1655 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1656
1657 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1659 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1660
1661 /* Link detection should happen immediately upon WM_SETTEXT */
1662 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1663 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1664 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1665 }
1666 DestroyWindow(hwndRichEdit);
1667
1668 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1669 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1670 hwndRichEdit = new_richedit(parent);
1671
1672 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1673 char * at_pos;
1674 int at_offset;
1675 int end_offset;
1676
1677 at_pos = strchr(templates_delim[j], 'X');
1678 at_offset = at_pos - templates_delim[j];
1679 strncpy(buffer, templates_delim[j], at_offset);
1680 buffer[at_offset] = '\0';
1681 strcat(buffer, urls[i].text);
1682 strcat(buffer, templates_delim[j] + at_offset + 1);
1683 end_offset = at_offset + strlen(urls[i].text);
1684
1685 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1687
1688 /* This assumes no templates start with the URL itself, and that they
1689 have at least two characters before the URL text */
1690 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1692 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1693 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1694 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1696
1697 if (urls[i].is_url)
1698 {
1699 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1700 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1701 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1702 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1703 }
1704 else
1705 {
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1710 }
1711 if (buffer[end_offset] != '\0')
1712 {
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1715 if (buffer[end_offset +1] != '\0')
1716 {
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1719 }
1720 }
1721 }
1722
1723 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1724 char * at_pos;
1725 int at_offset;
1726 int end_offset;
1727
1728 at_pos = strchr(templates_non_delim[j], 'X');
1729 at_offset = at_pos - templates_non_delim[j];
1730 strncpy(buffer, templates_non_delim[j], at_offset);
1731 buffer[at_offset] = '\0';
1732 strcat(buffer, urls[i].text);
1733 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1734 end_offset = at_offset + strlen(urls[i].text);
1735
1736 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1738
1739 /* This assumes no templates start with the URL itself, and that they
1740 have at least two characters before the URL text */
1741 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1742 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1747
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1752 if (buffer[end_offset] != '\0')
1753 {
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1756 if (buffer[end_offset +1] != '\0')
1757 {
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1760 }
1761 }
1762 }
1763
1764 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1765 char * at_pos;
1766 int at_offset;
1767 int end_offset;
1768
1769 at_pos = strchr(templates_xten_delim[j], 'X');
1770 at_offset = at_pos - templates_xten_delim[j];
1771 strncpy(buffer, templates_xten_delim[j], at_offset);
1772 buffer[at_offset] = '\0';
1773 strcat(buffer, urls[i].text);
1774 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1775 end_offset = at_offset + strlen(urls[i].text);
1776
1777 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1779
1780 /* This assumes no templates start with the URL itself, and that they
1781 have at least two characters before the URL text */
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1786 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1787 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1788
1789 if (urls[i].is_url)
1790 {
1791 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1793 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1794 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1795 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1796 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1797 }
1798 else
1799 {
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1806 }
1807 if (buffer[end_offset +1] != '\0')
1808 {
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1811 if (buffer[end_offset +2] != '\0')
1812 {
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1815 }
1816 }
1817 }
1818
1819 DestroyWindow(hwndRichEdit);
1820 hwndRichEdit = NULL;
1821 }
1822
1823 /* Test detection of URLs within normal text - WM_CHAR case. */
1824 /* Test only the first two URL examples for brevity */
1825 for (i = 0; i < 2; i++) {
1826 hwndRichEdit = new_richedit(parent);
1827
1828 /* Also for brevity, test only the first three delimiters */
1829 for (j = 0; j < 3; j++) {
1830 char * at_pos;
1831 int at_offset;
1832 int end_offset;
1833 int u, v;
1834
1835 at_pos = strchr(templates_delim[j], 'X');
1836 at_offset = at_pos - templates_delim[j];
1837 end_offset = at_offset + strlen(urls[i].text);
1838
1839 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1841 for (u = 0; templates_delim[j][u]; u++) {
1842 if (templates_delim[j][u] == '\r') {
1843 simulate_typing_characters(hwndRichEdit, "\r");
1844 } else if (templates_delim[j][u] != 'X') {
1845 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1846 } else {
1847 for (v = 0; urls[i].text[v]; v++) {
1848 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1849 }
1850 }
1851 }
1852 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1853
1854 /* This assumes no templates start with the URL itself, and that they
1855 have at least two characters before the URL text */
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1862
1863 if (urls[i].is_url)
1864 {
1865 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1867 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1869 }
1870 else
1871 {
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1874 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1875 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1876 }
1877 if (buffer[end_offset] != '\0')
1878 {
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1881 if (buffer[end_offset +1] != '\0')
1882 {
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1885 }
1886 }
1887
1888 /* The following will insert a paragraph break after the first character
1889 of the URL candidate, thus breaking the URL. It is expected that the
1890 CFE_LINK attribute should break across both pieces of the URL */
1891 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1892 simulate_typing_characters(hwndRichEdit, "\r");
1893 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1894
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1901
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1904 /* end_offset moved because of paragraph break */
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1907 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1908 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1909 {
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1912 if (buffer[end_offset +2] != '\0')
1913 {
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1916 }
1917 }
1918
1919 /* The following will remove the just-inserted paragraph break, thus
1920 restoring the URL */
1921 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1922 simulate_typing_characters(hwndRichEdit, "\b");
1923 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1924
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1931
1932 if (urls[i].is_url)
1933 {
1934 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1935 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1936 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1937 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1938 }
1939 else
1940 {
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1945 }
1946 if (buffer[end_offset] != '\0')
1947 {
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1950 if (buffer[end_offset +1] != '\0')
1951 {
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1954 }
1955 }
1956 }
1957 DestroyWindow(hwndRichEdit);
1958 hwndRichEdit = NULL;
1959 }
1960
1961 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1962 /* Test just the first two URL examples for brevity */
1963 for (i = 0; i < 2; i++) {
1964 SETTEXTEX st;
1965
1966 hwndRichEdit = new_richedit(parent);
1967
1968 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1969 be detected:
1970 1) Set entire text, a la WM_SETTEXT
1971 2) Set a selection of the text to the URL
1972 3) Set a portion of the text at a time, which eventually results in
1973 an URL
1974 All of them should give equivalent results
1975 */
1976
1977 /* Set entire text in one go, like WM_SETTEXT */
1978 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1979 char * at_pos;
1980 int at_offset;
1981 int end_offset;
1982
1983 st.codepage = CP_ACP;
1984 st.flags = ST_DEFAULT;
1985
1986 at_pos = strchr(templates_delim[j], 'X');
1987 at_offset = at_pos - templates_delim[j];
1988 strncpy(buffer, templates_delim[j], at_offset);
1989 buffer[at_offset] = '\0';
1990 strcat(buffer, urls[i].text);
1991 strcat(buffer, templates_delim[j] + at_offset + 1);
1992 end_offset = at_offset + strlen(urls[i].text);
1993
1994 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1995 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1996
1997 /* This assumes no templates start with the URL itself, and that they
1998 have at least two characters before the URL text */
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2005
2006 if (urls[i].is_url)
2007 {
2008 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2009 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2010 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2012 }
2013 else
2014 {
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2019 }
2020 if (buffer[end_offset] != '\0')
2021 {
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2024 if (buffer[end_offset +1] != '\0')
2025 {
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2028 }
2029 }
2030 }
2031
2032 /* Set selection with X to the URL */
2033 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2034 char * at_pos;
2035 int at_offset;
2036 int end_offset;
2037
2038 at_pos = strchr(templates_delim[j], 'X');
2039 at_offset = at_pos - templates_delim[j];
2040 end_offset = at_offset + strlen(urls[i].text);
2041
2042 st.codepage = CP_ACP;
2043 st.flags = ST_DEFAULT;
2044 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2045 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2046 st.flags = ST_SELECTION;
2047 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2048 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2049 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2050
2051 /* This assumes no templates start with the URL itself, and that they
2052 have at least two characters before the URL text */
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2059
2060 if (urls[i].is_url)
2061 {
2062 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2063 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2064 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2065 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2066 }
2067 else
2068 {
2069 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2070 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2071 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2072 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2073 }
2074 if (buffer[end_offset] != '\0')
2075 {
2076 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2077 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2078 if (buffer[end_offset +1] != '\0')
2079 {
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2082 }
2083 }
2084 }
2085
2086 /* Set selection with X to the first character of the URL, then the rest */
2087 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2088 char * at_pos;
2089 int at_offset;
2090 int end_offset;
2091
2092 at_pos = strchr(templates_delim[j], 'X');
2093 at_offset = at_pos - templates_delim[j];
2094 end_offset = at_offset + strlen(urls[i].text);
2095
2096 strcpy(buffer, "YY");
2097 buffer[0] = urls[i].text[0];
2098
2099 st.codepage = CP_ACP;
2100 st.flags = ST_DEFAULT;
2101 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2102 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2103 st.flags = ST_SELECTION;
2104 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2105 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2106 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2107 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2108 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2109
2110 /* This assumes no templates start with the URL itself, and that they
2111 have at least two characters before the URL text */
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2118
2119 if (urls[i].is_url)
2120 {
2121 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2125 }
2126 else
2127 {
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2132 }
2133 if (buffer[end_offset] != '\0')
2134 {
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137 if (buffer[end_offset +1] != '\0')
2138 {
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2141 }
2142 }
2143 }
2144
2145 DestroyWindow(hwndRichEdit);
2146 hwndRichEdit = NULL;
2147 }
2148
2149 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2150 /* Test just the first two URL examples for brevity */
2151 for (i = 0; i < 2; i++) {
2152 hwndRichEdit = new_richedit(parent);
2153
2154 /* Set selection with X to the URL */
2155 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2156 char * at_pos;
2157 int at_offset;
2158 int end_offset;
2159
2160 at_pos = strchr(templates_delim[j], 'X');
2161 at_offset = at_pos - templates_delim[j];
2162 end_offset = at_offset + strlen(urls[i].text);
2163
2164 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2165 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2166 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2167 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2168 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2169
2170 /* This assumes no templates start with the URL itself, and that they
2171 have at least two characters before the URL text */
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2174 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2175 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2178
2179 if (urls[i].is_url)
2180 {
2181 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2183 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2185 }
2186 else
2187 {
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2192 }
2193 if (buffer[end_offset] != '\0')
2194 {
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2197 if (buffer[end_offset +1] != '\0')
2198 {
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2201 }
2202 }
2203 }
2204
2205 /* Set selection with X to the first character of the URL, then the rest */
2206 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2207 char * at_pos;
2208 int at_offset;
2209 int end_offset;
2210
2211 at_pos = strchr(templates_delim[j], 'X');
2212 at_offset = at_pos - templates_delim[j];
2213 end_offset = at_offset + strlen(urls[i].text);
2214
2215 strcpy(buffer, "YY");
2216 buffer[0] = urls[i].text[0];
2217
2218 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2220 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2221 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2222 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2223 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2224 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2225
2226 /* This assumes no templates start with the URL itself, and that they
2227 have at least two characters before the URL text */
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2234
2235 if (urls[i].is_url)
2236 {
2237 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2238 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2239 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2240 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2241 }
2242 else
2243 {
2244 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2245 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2246 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2247 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2248 }
2249 if (buffer[end_offset] != '\0')
2250 {
2251 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2252 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2253 if (buffer[end_offset +1] != '\0')
2254 {
2255 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2256 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2257 }
2258 }
2259 }
2260
2261 DestroyWindow(hwndRichEdit);
2262 hwndRichEdit = NULL;
2263 }
2264
2265 DestroyWindow(parent);
2266 }
2267
2268 static void test_EM_SCROLL(void)
2269 {
2270 int i, j;
2271 int r; /* return value */
2272 int expr; /* expected return value */
2273 HWND hwndRichEdit = new_richedit(NULL);
2274 int y_before, y_after; /* units of lines of text */
2275
2276 /* test a richedit box containing a single line of text */
2277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2278 expr = 0x00010000;
2279 for (i = 0; i < 4; i++) {
2280 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2281
2282 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2283 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2284 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2285 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2286 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2287 "(i == %d)\n", y_after, i);
2288 }
2289
2290 /*
2291 * test a richedit box that will scroll. There are two general
2292 * cases: the case without any long lines and the case with a long
2293 * line.
2294 */
2295 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2296 if (i == 0)
2297 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2298 else
2299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2300 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2301 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2302 "LONG LINE \nb\nc\nd\ne");
2303 for (j = 0; j < 12; j++) /* reset scroll position to top */
2304 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2305
2306 /* get first visible line */
2307 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2308 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2309
2310 /* get new current first visible line */
2311 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2312
2313 ok(((r & 0xffffff00) == 0x00010000) &&
2314 ((r & 0x000000ff) != 0x00000000),
2315 "EM_SCROLL page down didn't scroll by a small positive number of "
2316 "lines (r == 0x%08x)\n", r);
2317 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2318 "(line %d scrolled to line %d\n", y_before, y_after);
2319
2320 y_before = y_after;
2321
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(((r & 0xffffff00) == 0x0001ff00),
2325 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2326 "(r == 0x%08x)\n", r);
2327 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2328 "%d scrolled to line %d\n", y_before, y_after);
2329
2330 y_before = y_after;
2331
2332 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2333
2334 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2335
2336 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2337 "(r == 0x%08x)\n", r);
2338 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2339 "1 line (%d scrolled to %d)\n", y_before, y_after);
2340
2341 y_before = y_after;
2342
2343 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2344
2345 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2346
2347 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2348 "(r == 0x%08x)\n", r);
2349 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2350 "line (%d scrolled to %d)\n", y_before, y_after);
2351
2352 y_before = y_after;
2353
2354 r = SendMessage(hwndRichEdit, EM_SCROLL,
2355 SB_LINEUP, 0); /* lineup beyond top */
2356
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2358
2359 ok(r == 0x00010000,
2360 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2361 ok(y_before == y_after,
2362 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2363
2364 y_before = y_after;
2365
2366 r = SendMessage(hwndRichEdit, EM_SCROLL,
2367 SB_PAGEUP, 0);/*page up beyond top */
2368
2369 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2370
2371 ok(r == 0x00010000,
2372 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2373 ok(y_before == y_after,
2374 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2375
2376 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2377 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2378 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2379 r = SendMessage(hwndRichEdit, EM_SCROLL,
2380 SB_PAGEDOWN, 0); /* page down beyond bot */
2381 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2382
2383 ok(r == 0x00010000,
2384 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2385 ok(y_before == y_after,
2386 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2387 y_before, y_after);
2388
2389 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2390 SendMessage(hwndRichEdit, EM_SCROLL,
2391 SB_LINEDOWN, 0); /* line down beyond bot */
2392 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2393
2394 ok(r == 0x00010000,
2395 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2396 ok(y_before == y_after,
2397 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2398 y_before, y_after);
2399 }
2400 DestroyWindow(hwndRichEdit);
2401 }
2402
2403 unsigned int recursionLevel = 0;
2404 unsigned int WM_SIZE_recursionLevel = 0;
2405 BOOL bailedOutOfRecursion = FALSE;
2406 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2407
2408 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2409 {
2410 LRESULT r;
2411
2412 if (bailedOutOfRecursion) return 0;
2413 if (recursionLevel >= 32) {
2414 bailedOutOfRecursion = TRUE;
2415 return 0;
2416 }
2417
2418 recursionLevel++;
2419 switch (message) {
2420 case WM_SIZE:
2421 WM_SIZE_recursionLevel++;
2422 r = richeditProc(hwnd, message, wParam, lParam);
2423 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2424 ShowScrollBar(hwnd, SB_VERT, TRUE);
2425 WM_SIZE_recursionLevel--;
2426 break;
2427 default:
2428 r = richeditProc(hwnd, message, wParam, lParam);
2429 break;
2430 }
2431 recursionLevel--;
2432 return r;
2433 }
2434
2435 static void test_scrollbar_visibility(void)
2436 {
2437 HWND hwndRichEdit;
2438 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2439 SCROLLINFO si;
2440 WNDCLASSA cls;
2441 BOOL r;
2442
2443 /* These tests show that richedit should temporarily refrain from automatically
2444 hiding or showing its scrollbars (vertical at least) when an explicit request
2445 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2446 Some applications depend on forced showing (when otherwise richedit would
2447 hide the vertical scrollbar) and are thrown on an endless recursive loop
2448 if richedit auto-hides the scrollbar again. Apparently they never heard of
2449 the ES_DISABLENOSCROLL style... */
2450
2451 hwndRichEdit = new_richedit(NULL);
2452
2453 /* Test default scrollbar visibility behavior */
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2461 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2462 si.nPage, si.nMin, si.nMax);
2463
2464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2465 memset(&si, 0, sizeof(si));
2466 si.cbSize = sizeof(si);
2467 si.fMask = SIF_PAGE | SIF_RANGE;
2468 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2469 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2470 "Vertical scrollbar is visible, should be invisible.\n");
2471 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2472 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2473 si.nPage, si.nMin, si.nMax);
2474
2475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2476 memset(&si, 0, sizeof(si));
2477 si.cbSize = sizeof(si);
2478 si.fMask = SIF_PAGE | SIF_RANGE;
2479 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2480 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2481 "Vertical scrollbar is invisible, should be visible.\n");
2482 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2483 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2484 si.nPage, si.nMin, si.nMax);
2485
2486 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2487 even though it hides the scrollbar */
2488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2489 memset(&si, 0, sizeof(si));
2490 si.cbSize = sizeof(si);
2491 si.fMask = SIF_PAGE | SIF_RANGE;
2492 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2493 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2494 "Vertical scrollbar is visible, should be invisible.\n");
2495 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2496 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2497 si.nPage, si.nMin, si.nMax);
2498
2499 /* Setting non-scrolling text again does *not* reset scrollbar range */
2500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2501 memset(&si, 0, sizeof(si));
2502 si.cbSize = sizeof(si);
2503 si.fMask = SIF_PAGE | SIF_RANGE;
2504 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2505 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2506 "Vertical scrollbar is visible, should be invisible.\n");
2507 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2508 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2509 si.nPage, si.nMin, si.nMax);
2510
2511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2512 memset(&si, 0, sizeof(si));
2513 si.cbSize = sizeof(si);
2514 si.fMask = SIF_PAGE | SIF_RANGE;
2515 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2516 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2517 "Vertical scrollbar is visible, should be invisible.\n");
2518 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2519 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2520 si.nPage, si.nMin, si.nMax);
2521
2522 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2523 memset(&si, 0, sizeof(si));
2524 si.cbSize = sizeof(si);
2525 si.fMask = SIF_PAGE | SIF_RANGE;
2526 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2527 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2528 "Vertical scrollbar is visible, should be invisible.\n");
2529 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2530 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2531 si.nPage, si.nMin, si.nMax);
2532
2533 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2534 memset(&si, 0, sizeof(si));
2535 si.cbSize = sizeof(si);
2536 si.fMask = SIF_PAGE | SIF_RANGE;
2537 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2538 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2539 "Vertical scrollbar is visible, should be invisible.\n");
2540 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2541 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2542 si.nPage, si.nMin, si.nMax);
2543
2544 DestroyWindow(hwndRichEdit);
2545
2546 /* Test again, with ES_DISABLENOSCROLL style */
2547 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2548
2549 /* Test default scrollbar visibility behavior */
2550 memset(&si, 0, sizeof(si));
2551 si.cbSize = sizeof(si);
2552 si.fMask = SIF_PAGE | SIF_RANGE;
2553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2554 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2555 "Vertical scrollbar is invisible, should be visible.\n");
2556 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2557 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2558 si.nPage, si.nMin, si.nMax);
2559
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2568 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2569 si.nPage, si.nMin, si.nMax);
2570
2571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2579 "reported page/range is %d (%d..%d)\n",
2580 si.nPage, si.nMin, si.nMax);
2581
2582 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2584 memset(&si, 0, sizeof(si));
2585 si.cbSize = sizeof(si);
2586 si.fMask = SIF_PAGE | SIF_RANGE;
2587 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2588 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2589 "Vertical scrollbar is invisible, should be visible.\n");
2590 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2591 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2592 si.nPage, si.nMin, si.nMax);
2593
2594 /* Setting non-scrolling text again does *not* reset scrollbar range */
2595 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2596 memset(&si, 0, sizeof(si));
2597 si.cbSize = sizeof(si);
2598 si.fMask = SIF_PAGE | SIF_RANGE;
2599 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2600 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2601 "Vertical scrollbar is invisible, should be visible.\n");
2602 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2603 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2604 si.nPage, si.nMin, si.nMax);
2605
2606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2607 memset(&si, 0, sizeof(si));
2608 si.cbSize = sizeof(si);
2609 si.fMask = SIF_PAGE | SIF_RANGE;
2610 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2611 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2612 "Vertical scrollbar is invisible, should be visible.\n");
2613 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2614 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2615 si.nPage, si.nMin, si.nMax);
2616
2617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2618 memset(&si, 0, sizeof(si));
2619 si.cbSize = sizeof(si);
2620 si.fMask = SIF_PAGE | SIF_RANGE;
2621 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2622 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2623 "Vertical scrollbar is invisible, should be visible.\n");
2624 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2625 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2626 si.nPage, si.nMin, si.nMax);
2627
2628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2629 memset(&si, 0, sizeof(si));
2630 si.cbSize = sizeof(si);
2631 si.fMask = SIF_PAGE | SIF_RANGE;
2632 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2633 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2634 "Vertical scrollbar is invisible, should be visible.\n");
2635 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2636 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2637 si.nPage, si.nMin, si.nMax);
2638
2639 DestroyWindow(hwndRichEdit);
2640
2641 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2642 hwndRichEdit = new_richedit(NULL);
2643
2644 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2645 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2651 "Vertical scrollbar is invisible, should be visible.\n");
2652 todo_wine {
2653 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2654 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2655 si.nPage, si.nMin, si.nMax);
2656 }
2657
2658 /* Ditto, see above */
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2666 todo_wine {
2667 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2668 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2669 si.nPage, si.nMin, si.nMax);
2670 }
2671
2672 /* Ditto, see above */
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2674 memset(&si, 0, sizeof(si));
2675 si.cbSize = sizeof(si);
2676 si.fMask = SIF_PAGE | SIF_RANGE;
2677 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2678 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2679 "Vertical scrollbar is invisible, should be visible.\n");
2680 todo_wine {
2681 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2682 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2683 si.nPage, si.nMin, si.nMax);
2684 }
2685
2686 /* Ditto, see above */
2687 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2693 "Vertical scrollbar is invisible, should be visible.\n");
2694 todo_wine {
2695 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2696 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2697 si.nPage, si.nMin, si.nMax);
2698 }
2699
2700 /* Ditto, see above */
2701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2702 memset(&si, 0, sizeof(si));
2703 si.cbSize = sizeof(si);
2704 si.fMask = SIF_PAGE | SIF_RANGE;
2705 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2706 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2707 "Vertical scrollbar is invisible, should be visible.\n");
2708 todo_wine {
2709 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2710 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2711 si.nPage, si.nMin, si.nMax);
2712 }
2713
2714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2715 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2716 memset(&si, 0, sizeof(si));
2717 si.cbSize = sizeof(si);
2718 si.fMask = SIF_PAGE | SIF_RANGE;
2719 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2720 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2721 "Vertical scrollbar is visible, should be invisible.\n");
2722 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2723 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2724 si.nPage, si.nMin, si.nMax);
2725
2726 DestroyWindow(hwndRichEdit);
2727
2728 hwndRichEdit = new_richedit(NULL);
2729
2730 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2731 memset(&si, 0, sizeof(si));
2732 si.cbSize = sizeof(si);
2733 si.fMask = SIF_PAGE | SIF_RANGE;
2734 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2735 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2736 "Vertical scrollbar is visible, should be invisible.\n");
2737 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2738 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2739 si.nPage, si.nMin, si.nMax);
2740
2741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2747 "Vertical scrollbar is visible, should be invisible.\n");
2748 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2749 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2750 si.nPage, si.nMin, si.nMax);
2751
2752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2753 memset(&si, 0, sizeof(si));
2754 si.cbSize = sizeof(si);
2755 si.fMask = SIF_PAGE | SIF_RANGE;
2756 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2758 "Vertical scrollbar is visible, should be invisible.\n");
2759 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2760 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2761 si.nPage, si.nMin, si.nMax);
2762
2763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2764 memset(&si, 0, sizeof(si));
2765 si.cbSize = sizeof(si);
2766 si.fMask = SIF_PAGE | SIF_RANGE;
2767 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2768 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2769 "Vertical scrollbar is visible, should be invisible.\n");
2770 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2771 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2772 si.nPage, si.nMin, si.nMax);
2773
2774 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2775 memset(&si, 0, sizeof(si));
2776 si.cbSize = sizeof(si);
2777 si.fMask = SIF_PAGE | SIF_RANGE;
2778 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2779 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2780 "Vertical scrollbar is invisible, should be visible.\n");
2781 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2782 "reported page/range is %d (%d..%d)\n",
2783 si.nPage, si.nMin, si.nMax);
2784
2785 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2786 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2787 memset(&si, 0, sizeof(si));
2788 si.cbSize = sizeof(si);
2789 si.fMask = SIF_PAGE | SIF_RANGE;
2790 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2791 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2792 "Vertical scrollbar is visible, should be invisible.\n");
2793 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2794 "reported page/range is %d (%d..%d)\n",
2795 si.nPage, si.nMin, si.nMax);
2796
2797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2803 "Vertical scrollbar is visible, should be invisible.\n");
2804 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2805 "reported page/range is %d (%d..%d)\n",
2806 si.nPage, si.nMin, si.nMax);
2807
2808 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2809 EM_SCROLL will make visible any forcefully invisible scrollbar */
2810 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2811 memset(&si, 0, sizeof(si));
2812 si.cbSize = sizeof(si);
2813 si.fMask = SIF_PAGE | SIF_RANGE;
2814 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2815 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2816 "Vertical scrollbar is invisible, should be visible.\n");
2817 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2818 "reported page/range is %d (%d..%d)\n",
2819 si.nPage, si.nMin, si.nMax);
2820
2821 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2822 memset(&si, 0, sizeof(si));
2823 si.cbSize = sizeof(si);
2824 si.fMask = SIF_PAGE | SIF_RANGE;
2825 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2826 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2827 "Vertical scrollbar is visible, should be invisible.\n");
2828 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2829 "reported page/range is %d (%d..%d)\n",
2830 si.nPage, si.nMin, si.nMax);
2831
2832 /* Again, EM_SCROLL, with SB_LINEUP */
2833 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2834 memset(&si, 0, sizeof(si));
2835 si.cbSize = sizeof(si);
2836 si.fMask = SIF_PAGE | SIF_RANGE;
2837 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2838 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2839 "Vertical scrollbar is invisible, should be visible.\n");
2840 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2841 "reported page/range is %d (%d..%d)\n",
2842 si.nPage, si.nMin, si.nMax);
2843
2844 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2845 memset(&si, 0, sizeof(si));
2846 si.cbSize = sizeof(si);
2847 si.fMask = SIF_PAGE | SIF_RANGE;
2848 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2849 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2850 "Vertical scrollbar is visible, should be invisible.\n");
2851 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2852 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2853 si.nPage, si.nMin, si.nMax);
2854
2855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2856 memset(&si, 0, sizeof(si));
2857 si.cbSize = sizeof(si);
2858 si.fMask = SIF_PAGE | SIF_RANGE;
2859 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2860 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2861 "Vertical scrollbar is invisible, should be visible.\n");
2862 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2863 "reported page/range is %d (%d..%d)\n",
2864 si.nPage, si.nMin, si.nMax);
2865
2866 DestroyWindow(hwndRichEdit);
2867
2868
2869 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2870 hwndRichEdit = new_richedit(NULL);
2871
2872 #define ENABLE_WS_VSCROLL(hwnd) \
2873 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2874 #define DISABLE_WS_VSCROLL(hwnd) \
2875 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2876
2877 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2878 ENABLE_WS_VSCROLL(hwndRichEdit);
2879 memset(&si, 0, sizeof(si));
2880 si.cbSize = sizeof(si);
2881 si.fMask = SIF_PAGE | SIF_RANGE;
2882 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2883 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2884 "Vertical scrollbar is invisible, should be visible.\n");
2885 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2886 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2887 si.nPage, si.nMin, si.nMax);
2888
2889 /* Ditto, see above */
2890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2891 memset(&si, 0, sizeof(si));
2892 si.cbSize = sizeof(si);
2893 si.fMask = SIF_PAGE | SIF_RANGE;
2894 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2895 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2896 "Vertical scrollbar is invisible, should be visible.\n");
2897 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2898 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2899 si.nPage, si.nMin, si.nMax);
2900
2901 /* Ditto, see above */
2902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2903 memset(&si, 0, sizeof(si));
2904 si.cbSize = sizeof(si);
2905 si.fMask = SIF_PAGE | SIF_RANGE;
2906 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2907 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2908 "Vertical scrollbar is invisible, should be visible.\n");
2909 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2910 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2911 si.nPage, si.nMin, si.nMax);
2912
2913 /* Ditto, see above */
2914 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2920 "Vertical scrollbar is invisible, should be visible.\n");
2921 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923 si.nPage, si.nMin, si.nMax);
2924
2925 /* Ditto, see above */
2926 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2927 memset(&si, 0, sizeof(si));
2928 si.cbSize = sizeof(si);
2929 si.fMask = SIF_PAGE | SIF_RANGE;
2930 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2931 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2932 "Vertical scrollbar is invisible, should be visible.\n");
2933 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2934 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2935 si.nPage, si.nMin, si.nMax);
2936
2937 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2938 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2939 memset(&si, 0, sizeof(si));
2940 si.cbSize = sizeof(si);
2941 si.fMask = SIF_PAGE | SIF_RANGE;
2942 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2943 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2944 "Vertical scrollbar is visible, should be invisible.\n");
2945 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2946 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2947 si.nPage, si.nMin, si.nMax);
2948
2949 DestroyWindow(hwndRichEdit);
2950
2951 hwndRichEdit = new_richedit(NULL);
2952
2953 DISABLE_WS_VSCROLL(hwndRichEdit);
2954 memset(&si, 0, sizeof(si));
2955 si.cbSize = sizeof(si);
2956 si.fMask = SIF_PAGE | SIF_RANGE;
2957 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2958 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2959 "Vertical scrollbar is visible, should be invisible.\n");
2960 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2961 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2962 si.nPage, si.nMin, si.nMax);
2963
2964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2965 memset(&si, 0, sizeof(si));
2966 si.cbSize = sizeof(si);
2967 si.fMask = SIF_PAGE | SIF_RANGE;
2968 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2969 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2970 "Vertical scrollbar is visible, should be invisible.\n");
2971 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2972 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2973 si.nPage, si.nMin, si.nMax);
2974
2975 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2976 memset(&si, 0, sizeof(si));
2977 si.cbSize = sizeof(si);
2978 si.fMask = SIF_PAGE | SIF_RANGE;
2979 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2981 "Vertical scrollbar is visible, should be invisible.\n");
2982 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2983 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2984 si.nPage, si.nMin, si.nMax);
2985
2986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2992 "Vertical scrollbar is visible, should be invisible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2996
2997 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2998 memset(&si, 0, sizeof(si));
2999 si.cbSize = sizeof(si);
3000 si.fMask = SIF_PAGE | SIF_RANGE;
3001 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3002 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3003 "Vertical scrollbar is invisible, should be visible.\n");
3004 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3005 "reported page/range is %d (%d..%d)\n",
3006 si.nPage, si.nMin, si.nMax);
3007
3008 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3009 DISABLE_WS_VSCROLL(hwndRichEdit);
3010 memset(&si, 0, sizeof(si));
3011 si.cbSize = sizeof(si);
3012 si.fMask = SIF_PAGE | SIF_RANGE;
3013 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3014 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3015 "Vertical scrollbar is visible, should be invisible.\n");
3016 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3017 "reported page/range is %d (%d..%d)\n",
3018 si.nPage, si.nMin, si.nMax);
3019
3020 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3021 memset(&si, 0, sizeof(si));
3022 si.cbSize = sizeof(si);
3023 si.fMask = SIF_PAGE | SIF_RANGE;
3024 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3025 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3026 "Vertical scrollbar is visible, should be invisible.\n");
3027 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3028 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3029 si.nPage, si.nMin, si.nMax);
3030
3031 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3032 memset(&si, 0, sizeof(si));
3033 si.cbSize = sizeof(si);
3034 si.fMask = SIF_PAGE | SIF_RANGE;
3035 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3036 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3037 "Vertical scrollbar is invisible, should be visible.\n");
3038 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3039 "reported page/range is %d (%d..%d)\n",
3040 si.nPage, si.nMin, si.nMax);
3041
3042 DISABLE_WS_VSCROLL(hwndRichEdit);
3043 memset(&si, 0, sizeof(si));
3044 si.cbSize = sizeof(si);
3045 si.fMask = SIF_PAGE | SIF_RANGE;
3046 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3047 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3048 "Vertical scrollbar is visible, should be invisible.\n");
3049 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3050 "reported page/range is %d (%d..%d)\n",
3051 si.nPage, si.nMin, si.nMax);
3052
3053 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3054 EM_SCROLL will make visible any forcefully invisible scrollbar */
3055 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3056 memset(&si, 0, sizeof(si));
3057 si.cbSize = sizeof(si);
3058 si.fMask = SIF_PAGE | SIF_RANGE;
3059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3060 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3061 "Vertical scrollbar is invisible, should be visible.\n");
3062 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3063 "reported page/range is %d (%d..%d)\n",
3064 si.nPage, si.nMin, si.nMax);
3065
3066 DISABLE_WS_VSCROLL(hwndRichEdit);
3067 memset(&si, 0, sizeof(si));
3068 si.cbSize = sizeof(si);
3069 si.fMask = SIF_PAGE | SIF_RANGE;
3070 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3071 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3072 "Vertical scrollbar is visible, should be invisible.\n");
3073 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3074 "reported page/range is %d (%d..%d)\n",
3075 si.nPage, si.nMin, si.nMax);
3076
3077 /* Again, EM_SCROLL, with SB_LINEUP */
3078 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084 "Vertical scrollbar is invisible, should be visible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3086 "reported page/range is %d (%d..%d)\n",
3087 si.nPage, si.nMin, si.nMax);
3088
3089 DestroyWindow(hwndRichEdit);
3090
3091 /* This window proc models what is going on with Corman Lisp 3.0.
3092 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3093 force the scrollbar into visibility. Recursion should NOT happen
3094 as a result of this action.
3095 */
3096 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3097 if (r) {
3098 richeditProc = cls.lpfnWndProc;
3099 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3100 cls.lpszClassName = "RicheditStupidOverride";
3101 if(!RegisterClassA(&cls)) assert(0);
3102
3103 recursionLevel = 0;
3104 WM_SIZE_recursionLevel = 0;
3105 bailedOutOfRecursion = FALSE;
3106 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3107 ok(!bailedOutOfRecursion,
3108 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3109
3110 recursionLevel = 0;
3111 WM_SIZE_recursionLevel = 0;
3112 bailedOutOfRecursion = FALSE;
3113 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3114 ok(!bailedOutOfRecursion,
3115 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3116
3117 /* Unblock window in order to process WM_DESTROY */
3118 recursionLevel = 0;
3119 bailedOutOfRecursion = FALSE;
3120 WM_SIZE_recursionLevel = 0;
3121 DestroyWindow(hwndRichEdit);
3122 }
3123 }
3124
3125 static void test_EM_SETUNDOLIMIT(void)
3126 {
3127 /* cases we test for:
3128 * default behaviour - limiting at 100 undo's
3129 * undo disabled - setting a limit of 0
3130 * undo limited - undo limit set to some to some number, like 2
3131 * bad input - sending a negative number should default to 100 undo's */
3132
3133 HWND hwndRichEdit = new_richedit(NULL);
3134 CHARRANGE cr;
3135 int i;
3136 int result;
3137
3138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3139 cr.cpMin = 0;
3140 cr.cpMax = 1;
3141 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3142 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3143 also, multiple pastes don't combine like WM_CHAR would */
3144 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3145
3146 /* first case - check the default */
3147 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3148 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3149 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3150 for (i=0; i<100; i++) /* Undo 100 of them */
3151 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3152 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3153 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3154
3155 /* second case - cannot undo */
3156 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3157 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3158 SendMessage(hwndRichEdit,
3159 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3160 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3161 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3162
3163 /* third case - set it to an arbitrary number */
3164 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3165 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3166 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3167 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3168 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3169 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3170 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3171 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3172 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3173 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3174 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3175 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3176 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3177 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3178
3179 /* fourth case - setting negative numbers should default to 100 undos */
3180 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3181 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3182 ok (result == 100,
3183 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3184
3185 DestroyWindow(hwndRichEdit);
3186 }
3187
3188 static void test_ES_PASSWORD(void)
3189 {
3190 /* This isn't hugely testable, so we're just going to run it through its paces */
3191
3192 HWND hwndRichEdit = new_richedit(NULL);
3193 WCHAR result;
3194
3195 /* First, check the default of a regular control */
3196 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3197 ok (result == 0,
3198 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3199
3200 /* Now, set it to something normal */
3201 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3202 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3203 ok (result == 120,
3204 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3205
3206 /* Now, set it to something odd */
3207 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3208 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3209 ok (result == 1234,
3210 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3211 DestroyWindow(hwndRichEdit);
3212 }
3213
3214 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3215 LPBYTE pbBuff,
3216 LONG cb,
3217 LONG *pcb)
3218 {
3219 char** str = (char**)dwCookie;
3220 *pcb = cb;
3221 if (*pcb > 0) {
3222 memcpy(*str, pbBuff, *pcb);
3223 *str += *pcb;
3224 }
3225 return 0;
3226 }
3227
3228 static void test_WM_SETTEXT()
3229 {
3230 HWND hwndRichEdit = new_richedit(NULL);
3231 const char * TestItem1 = "TestSomeText";
3232 const char * TestItem2 = "TestSomeText\r";
3233 const char * TestItem2_after = "TestSomeText\r\n";
3234 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3235 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3236 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3237 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3238 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3239 const char * TestItem5_after = "TestSomeText TestSomeText";
3240 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3241 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3242 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3243 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3244
3245 char buf[1024] = {0};
3246 LRESULT result;
3247 EDITSTREAM es;
3248 char * p;
3249
3250 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3251 any solitary \r to be converted to \r\n on return. Properly paired
3252 \r\n are not affected. It also shows that the special sequence \r\r\n
3253 gets converted to a single space.
3254 */
3255
3256 #define TEST_SETTEXT(a, b) \
3257 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3258 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3259 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3260 ok (result == lstrlen(buf), \
3261 "WM_GETTEXT returned %ld instead of expected %u\n", \
3262 result, lstrlen(buf)); \
3263 result = strcmp(b, buf); \
3264 ok(result == 0, \
3265 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3266
3267 TEST_SETTEXT(TestItem1, TestItem1)
3268 TEST_SETTEXT(TestItem2, TestItem2_after)
3269 TEST_SETTEXT(TestItem3, TestItem3_after)
3270 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3271 TEST_SETTEXT(TestItem4, TestItem4_after)
3272 TEST_SETTEXT(TestItem5, TestItem5_after)
3273 TEST_SETTEXT(TestItem6, TestItem6_after)
3274 TEST_SETTEXT(TestItem7, TestItem7_after)
3275
3276 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3278 p = buf;
3279 es.dwCookie = (DWORD_PTR)&p;
3280 es.dwError = 0;
3281 es.pfnCallback = test_WM_SETTEXT_esCallback;
3282 memset(buf, 0, sizeof(buf));
3283 SendMessage(hwndRichEdit, EM_STREAMOUT,
3284 (WPARAM)(SF_RTF), (LPARAM)&es);
3285 trace("EM_STREAMOUT produced: \n%s\n", buf);
3286 TEST_SETTEXT(buf, TestItem1)
3287
3288 #undef TEST_SETTEXT
3289 DestroyWindow(hwndRichEdit);
3290 }
3291
3292 static void test_EM_STREAMOUT(void)
3293 {
3294 HWND hwndRichEdit = new_richedit(NULL);
3295 int r;
3296 EDITSTREAM es;
3297 char buf[1024] = {0};
3298 char * p;
3299
3300 const char * TestItem1 = "TestSomeText";
3301 const char * TestItem2 = "TestSomeText\r";
3302 const char * TestItem3 = "TestSomeText\r\n";
3303
3304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3305 p = buf;
3306 es.dwCookie = (DWORD_PTR)&p;
3307 es.dwError = 0;
3308 es.pfnCallback = test_WM_SETTEXT_esCallback;
3309 memset(buf, 0, sizeof(buf));
3310 SendMessage(hwndRichEdit, EM_STREAMOUT,
3311 (WPARAM)(SF_TEXT), (LPARAM)&es);
3312 r = strlen(buf);
3313 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3314 ok(strcmp(buf, TestItem1) == 0,
3315 "streamed text different, got %s\n", buf);
3316
3317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3318 p = buf;
3319 es.dwCookie = (DWORD_PTR)&p;
3320 es.dwError = 0;
3321 es.pfnCallback = test_WM_SETTEXT_esCallback;
3322 memset(buf, 0, sizeof(buf));
3323 SendMessage(hwndRichEdit, EM_STREAMOUT,
3324 (WPARAM)(SF_TEXT), (LPARAM)&es);
3325 r = strlen(buf);
3326 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3327 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3328 ok(strcmp(buf, TestItem3) == 0,
3329 "streamed text different from, got %s\n", buf);
3330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3331 p = buf;
3332 es.dwCookie = (DWORD_PTR)&p;
3333 es.dwError = 0;
3334 es.pfnCallback = test_WM_SETTEXT_esCallback;
3335 memset(buf, 0, sizeof(buf));
3336 SendMessage(hwndRichEdit, EM_STREAMOUT,
3337 (WPARAM)(SF_TEXT), (LPARAM)&es);
3338 r = strlen(buf);
3339 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3340 ok(strcmp(buf, TestItem3) == 0,
3341 "streamed text different, got %s\n", buf);
3342
3343 DestroyWindow(hwndRichEdit);
3344 }
3345
3346 static void test_EM_SETTEXTEX(void)
3347 {
3348 HWND hwndRichEdit = new_richedit(NULL);
3349 SETTEXTEX setText;
3350 GETTEXTEX getText;
3351 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3352 'S', 'o', 'm', 'e',
3353 'T', 'e', 'x', 't', 0};
3354 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3355 't', 'S', 'o', 'm',
3356 'e', 'T', 'e', 'x',
3357 't', 't', 'S', 'o',
3358 'm', 'e', 'T', 'e',
3359 'x', 't', 0};
3360 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3361 '\r','t','S','o','m','e','T','e','x','t',0};
3362 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3363 'S', 'o', 'm', 'e',
3364 'T', 'e', 'x', 't',
3365 '\r', 0};
3366 const char * TestItem2_after = "TestSomeText\r\n";
3367 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3368 'S', 'o', 'm', 'e',
3369 'T', 'e', 'x', 't',
3370 '\r','\n','\r','\n', 0};
3371 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3372 'S', 'o', 'm', 'e',
3373 'T', 'e', 'x', 't',
3374 '\n','\n', 0};
3375 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3376 'S', 'o', 'm', 'e',
3377 'T', 'e', 'x', 't',
3378 '\r','\r', 0};
3379 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3380 'S', 'o', 'm', 'e',
3381 'T', 'e', 'x', 't',
3382 '\r','\r','\n','\r',
3383 '\n', 0};
3384 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3385 'S', 'o', 'm', 'e',
3386 'T', 'e', 'x', 't',
3387 ' ','\r', 0};
3388 #define MAX_BUF_LEN 1024
3389 WCHAR buf[MAX_BUF_LEN];
3390 char bufACP[MAX_BUF_LEN];
3391 char * p;
3392 int result;
3393 CHARRANGE cr;
3394 EDITSTREAM es;
3395
3396 setText.codepage = 1200; /* no constant for unicode */
3397 getText.codepage = 1200; /* no constant for unicode */
3398 getText.cb = MAX_BUF_LEN;
3399 getText.flags = GT_DEFAULT;
3400 getText.lpDefaultChar = NULL;
3401 getText.lpUsedDefChar = NULL;
3402
3403 setText.flags = 0;
3404 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3406 ok(lstrcmpW(buf, TestItem1) == 0,
3407 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3408
3409 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3410 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3411 */
3412 setText.codepage = 1200; /* no constant for unicode */
3413 getText.codepage = 1200; /* no constant for unicode */
3414 getText.cb = MAX_BUF_LEN;
3415 getText.flags = GT_DEFAULT;
3416 getText.lpDefaultChar = NULL;
3417 getText.lpUsedDefChar = NULL;
3418 setText.flags = 0;
3419 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3420 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3421 ok(lstrcmpW(buf, TestItem2) == 0,
3422 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3423
3424 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3425 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3426 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3427 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3428
3429 /* Baseline test for just-enough buffer space for string */
3430 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3431 getText.codepage = 1200; /* no constant for unicode */
3432 getText.flags = GT_DEFAULT;
3433 getText.lpDefaultChar = NULL;
3434 getText.lpUsedDefChar = NULL;
3435 memset(buf, 0, MAX_BUF_LEN);
3436 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3437 ok(lstrcmpW(buf, TestItem2) == 0,
3438 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3439
3440 /* When there is enough space for one character, but not both, of the CRLF
3441 pair at the end of the string, the CR is not copied at all. That is,
3442 the caller must not see CRLF pairs truncated to CR at the end of the
3443 string.
3444 */
3445 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3446 getText.codepage = 1200; /* no constant for unicode */
3447 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3448 getText.lpDefaultChar = NULL;
3449 getText.lpUsedDefChar = NULL;
3450 memset(buf, 0, MAX_BUF_LEN);
3451 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3452 ok(lstrcmpW(buf, TestItem1) == 0,
3453 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3454
3455
3456 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3457 setText.codepage = 1200; /* no constant for unicode */
3458 getText.codepage = 1200; /* no constant for unicode */
3459 getText.cb = MAX_BUF_LEN;
3460 getText.flags = GT_DEFAULT;
3461 getText.lpDefaultChar = NULL;
3462 getText.lpUsedDefChar = NULL;
3463 setText.flags = 0;
3464 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3465 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3466 ok(lstrcmpW(buf, TestItem3_after) == 0,
3467 "EM_SETTEXTEX did not convert properly\n");
3468
3469 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3470 setText.codepage = 1200; /* no constant for unicode */
3471 getText.codepage = 1200; /* no constant for unicode */
3472 getText.cb = MAX_BUF_LEN;
3473 getText.flags = GT_DEFAULT;
3474 getText.lpDefaultChar = NULL;
3475 getText.lpUsedDefChar = NULL;
3476 setText.flags = 0;
3477 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3478 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3479 ok(lstrcmpW(buf, TestItem3_after) == 0,
3480 "EM_SETTEXTEX did not convert properly\n");
3481
3482 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3483 setText.codepage = 1200; /* no constant for unicode */
3484 getText.codepage = 1200; /* no constant for unicode */
3485 getText.cb = MAX_BUF_LEN;
3486 getText.flags = GT_DEFAULT;
3487 getText.lpDefaultChar = NULL;
3488 getText.lpUsedDefChar = NULL;
3489 setText.flags = 0;
3490 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3491 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3492 ok(lstrcmpW(buf, TestItem4_after) == 0,
3493 "EM_SETTEXTEX did not convert properly\n");
3494
3495 /* !ST_SELECTION && Unicode && !\rtf */
3496 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3497 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3498
3499 ok (result == 1,
3500 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3501 ok(lstrlenW(buf) == 0,
3502 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3503
3504 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3505 setText.flags = 0;
3506 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3507 /* select some text */
3508 cr.cpMax = 1;
3509 cr.cpMin = 3;
3510 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3511 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3512 setText.flags = ST_SELECTION;
3513 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3514 ok(result == 0,
3515 "EM_SETTEXTEX with NULL lParam to replace selection"
3516 " with no text should return 0. Got %i\n",
3517 result);
3518
3519 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3520 setText.flags = 0;
3521 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3522 /* select some text */
3523 cr.cpMax = 1;
3524 cr.cpMin = 3;
3525 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3526 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3527 setText.flags = ST_SELECTION;
3528 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3529 (WPARAM)&setText, (LPARAM) TestItem1);
3530 /* get text */
3531 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3532 ok(result == lstrlenW(TestItem1),
3533 "EM_SETTEXTEX with NULL lParam to replace selection"
3534 " with no text should return 0. Got %i\n",
3535 result);
3536 ok(lstrlenW(buf) == 22,
3537 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3538 lstrlenW(buf) );
3539
3540 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3541 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3542 p = (char *)buf;
3543 es.dwCookie = (DWORD_PTR)&p;
3544 es.dwError = 0;
3545 es.pfnCallback = test_WM_SETTEXT_esCallback;
3546 memset(buf, 0, sizeof(buf));
3547 SendMessage(hwndRichEdit, EM_STREAMOUT,
3548 (WPARAM)(SF_RTF), (LPARAM)&es);
3549 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3550
3551 /* !ST_SELECTION && !Unicode && \rtf */
3552 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3553 getText.codepage = 1200; /* no constant for unicode */
3554 getText.cb = MAX_BUF_LEN;
3555 getText.flags = GT_DEFAULT;
3556 getText.lpDefaultChar = NULL;
3557 getText.lpUsedDefChar = NULL;
3558
3559 setText.flags = 0;
3560 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3561 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3562 ok(lstrcmpW(buf, TestItem1) == 0,
3563 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3564
3565 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3566 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3567 p = (char *)buf;
3568 es.dwCookie = (DWORD_PTR)&p;
3569 es.dwError = 0;
3570 es.pfnCallback = test_WM_SETTEXT_esCallback;
3571 memset(buf, 0, sizeof(buf));
3572 SendMessage(hwndRichEdit, EM_STREAMOUT,
3573 (WPARAM)(SF_RTF), (LPARAM)&es);
3574 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3575
3576 /* select some text */
3577 cr.cpMax = 1;
3578 cr.cpMin = 3;
3579 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3580
3581 /* ST_SELECTION && !Unicode && \rtf */
3582 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3583 getText.codepage = 1200; /* no constant for unicode */
3584 getText.cb = MAX_BUF_LEN;
3585 getText.flags = GT_DEFAULT;
3586 getText.lpDefaultChar = NULL;
3587 getText.lpUsedDefChar = NULL;
3588
3589 setText.flags = ST_SELECTION;
3590 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3591 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3592 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3593
3594 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3595 setText.codepage = 1200; /* no constant for unicode */
3596 getText.codepage = CP_ACP;
3597 getText.cb = MAX_BUF_LEN;
3598
3599 setText.flags = 0;
3600 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3601 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3602
3603 /* select some text */
3604 cr.cpMax = 1;
3605 cr.cpMin = 3;
3606 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3607
3608 /* ST_SELECTION && !Unicode && !\rtf */
3609 setText.codepage = CP_ACP;
3610 getText.codepage = 1200; /* no constant for unicode */
3611 getText.cb = MAX_BUF_LEN;
3612 getText.flags = GT_DEFAULT;
3613 getText.lpDefaultChar = NULL;
3614 getText.lpUsedDefChar = NULL;
3615
3616 setText.flags = ST_SELECTION;
3617 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3618 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3619 ok(lstrcmpW(buf, TestItem1alt) == 0,
3620 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3621 " using ST_SELECTION and non-Unicode\n");
3622
3623 /* Test setting text using rich text format */
3624 setText.flags = 0;
3625 setText.codepage = CP_ACP;
3626 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3627 getText.codepage = CP_ACP;
3628 getText.cb = MAX_BUF_LEN;
3629 getText.flags = GT_DEFAULT;
3630 getText.lpDefaultChar = NULL;
3631 getText.lpUsedDefChar = NULL;
3632 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3633 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3634
3635 setText.flags = 0;
3636 setText.codepage = CP_ACP;
3637 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3638 getText.codepage = CP_ACP;
3639 getText.cb = MAX_BUF_LEN;
3640 getText.flags = GT_DEFAULT;
3641 getText.lpDefaultChar = NULL;
3642 getText.lpUsedDefChar = NULL;
3643 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3644 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3645
3646 DestroyWindow(hwndRichEdit);
3647 }
3648
3649 static void test_EM_LIMITTEXT(void)
3650 {
3651 int ret;
3652
3653 HWND hwndRichEdit = new_richedit(NULL);
3654
3655 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3656 * about setting the length to -1 for multiline edit controls doesn't happen.
3657 */
3658
3659 /* Don't check default gettextlimit case. That's done in other tests */
3660
3661 /* Set textlimit to 100 */
3662 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3663 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3664 ok (ret == 100,
3665 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3666
3667 /* Set textlimit to 0 */
3668 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3669 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3670 ok (ret == 65536,
3671 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3672
3673 /* Set textlimit to -1 */
3674 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3675 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3676 ok (ret == -1,
3677 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3678
3679 /* Set textlimit to -2 */
3680 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3681 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3682 ok (ret == -2,
3683 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3684
3685 DestroyWindow (hwndRichEdit);
3686 }
3687
3688
3689 static void test_EM_EXLIMITTEXT(void)
3690 {
3691 int i, selBegin, selEnd, len1, len2;
3692 int result;
3693 char text[1024 + 1];
3694 char buffer[1024 + 1];
3695 int textlimit = 0; /* multiple of 100 */
3696 HWND hwndRichEdit = new_richedit(NULL);
3697
3698 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3699 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3700
3701 textlimit = 256000;
3702 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3703 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3704 /* set higher */
3705 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3706
3707 textlimit = 1000;
3708 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3709 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3710 /* set lower */
3711 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3712
3713 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3714 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3715 /* default for WParam = 0 */
3716 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3717
3718 textlimit = sizeof(text)-1;
3719 memset(text, 'W', textlimit);
3720 text[sizeof(text)-1] = 0;
3721 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3722 /* maxed out text */
3723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3724
3725 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3726 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3727 len1 = selEnd - selBegin;
3728
3729 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3730 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3731 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3732 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3733 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3734 len2 = selEnd - selBegin;
3735
3736 ok(len1 != len2,
3737 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3738 len1,len2,i);
3739
3740 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3741 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3742 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3743 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3744 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3745 len1 = selEnd - selBegin;
3746
3747 ok(len1 != len2,
3748 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3749 len1,len2,i);
3750
3751 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3752 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3753 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3754 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3755 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3756 len2 = selEnd - selBegin;
3757
3758 ok(len1 == len2,
3759 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3760 len1,len2,i);
3761
3762 /* set text up to the limit, select all the text, then add a char */
3763 textlimit = 5;
3764 memset(text, 'W', textlimit);
3765 text[textlimit] = 0;
3766 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3768 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3769 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3770 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3771 result = strcmp(buffer, "A");
3772 ok(0 == result, "got string = \"%s\"\n", buffer);
3773
3774 /* WM_SETTEXT not limited */
3775 textlimit = 10;
3776 memset(text, 'W', textlimit);
3777 text[textlimit] = 0;
3778 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3780 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3781 i = strlen(buffer);
3782 ok(10 == i, "expected 10 chars\n");
3783 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3784 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3785
3786 /* try inserting more text at end */
3787 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3788 ok(0 == i, "WM_CHAR wasn't processed\n");
3789 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3790 i = strlen(buffer);
3791 ok(10 == i, "expected 10 chars, got %i\n", i);
3792 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3793 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3794
3795 /* try inserting text at beginning */
3796 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3797 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3798 ok(0 == i, "WM_CHAR wasn't processed\n");
3799 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3800 i = strlen(buffer);
3801 ok(10 == i, "expected 10 chars, got %i\n", i);
3802 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3803 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3804
3805 /* WM_CHAR is limited */
3806 textlimit = 1;
3807 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3808 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3809 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3810 ok(0 == i, "WM_CHAR wasn't processed\n");
3811 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3812 ok(0 == i, "WM_CHAR wasn't processed\n");
3813 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3814 i = strlen(buffer);
3815 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3816
3817 DestroyWindow(hwndRichEdit);
3818 }
3819
3820 static void test_EM_GETLIMITTEXT(void)
3821 {
3822 int i;
3823 HWND hwndRichEdit = new_richedit(NULL);
3824
3825 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3826 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3827
3828 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3829 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3830 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3831
3832 DestroyWindow(hwndRichEdit);
3833 }
3834
3835 static void test_WM_SETFONT(void)
3836 {
3837 /* There is no invalid input or error conditions for this function.
3838 * NULL wParam and lParam just fall back to their default values
3839 * It should be noted that even if you use a gibberish name for your fonts
3840 * here, it will still work because the name is stored. They will display as
3841 * System, but will report their name to be whatever they were created as */
3842
3843 HWND hwndRichEdit = new_richedit(NULL);
3844 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3845 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3846 FF_DONTCARE, "Marlett");
3847 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3848 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3849 FF_DONTCARE, "MS Sans Serif");
3850 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3851 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3852 FF_DONTCARE, "Courier");
3853 LOGFONTA sentLogFont;
3854 CHARFORMAT2A returnedCF2A;
3855
3856 returnedCF2A.cbSize = sizeof(returnedCF2A);
3857
3858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3859 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3860 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3861
3862 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3863 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3864 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3865 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3866
3867 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3868 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3869 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3870 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3871 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3872 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3873
3874 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
3875 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3876 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3877 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3878 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3879 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3880
3881 /* This last test is special since we send in NULL. We clear the variables
3882 * and just compare to "System" instead of the sent in font name. */
3883 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3884 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3885 returnedCF2A.cbSize = sizeof(returnedCF2A);
3886
3887 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
3888 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3889 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3890 ok (!strcmp("System",returnedCF2A.szFaceName),
3891 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3892
3893 DestroyWindow(hwndRichEdit);
3894 }
3895
3896
3897 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3898 LPBYTE pbBuff,
3899 LONG cb,
3900 LONG *pcb)
3901 {
3902 const char** str = (const char**)dwCookie;
3903 int size = strlen(*str);
3904 if(size > 3) /* let's make it piecemeal for fun */
3905 size = 3;
3906 *pcb = cb;
3907 if (*pcb > size) {
3908 *pcb = size;
3909 }
3910 if (*pcb > 0) {
3911 memcpy(pbBuff, *str, *pcb);
3912 *str += *pcb;
3913 }
3914 return 0;
3915 }
3916
3917 static void test_EM_GETMODIFY(void)
3918 {
3919 HWND hwndRichEdit = new_richedit(NULL);
3920 LRESULT result;
3921 SETTEXTEX setText;
3922 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3923 'S', 'o', 'm', 'e',
3924 'T', 'e', 'x', 't', 0};
3925 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3926 'S', 'o', 'm', 'e',
3927 'O', 't', 'h', 'e', 'r',
3928 'T', 'e', 'x', 't', 0};
3929 const char* streamText = "hello world";
3930 CHARFORMAT2 cf2;
3931 PARAFORMAT2 pf2;
3932 EDITSTREAM es;
3933
3934 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3935 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3936 FF_DONTCARE, "Courier");
3937
3938 setText.codepage = 1200; /* no constant for unicode */
3939 setText.flags = ST_KEEPUNDO;
3940
3941
3942 /* modify flag shouldn't be set when richedit is first created */
3943 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3944 ok (result == 0,
3945 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3946
3947 /* setting modify flag should actually set it */
3948 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3949 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3950 ok (result != 0,
3951 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3952
3953 /* clearing modify flag should actually clear it */
3954 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3956 ok (result == 0,
3957 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3958
3959 /* setting font doesn't change modify flag */
3960 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3961 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
3962 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3963 ok (result == 0,
3964 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3965
3966 /* setting text should set modify flag */
3967 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3968 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3969 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3970 ok (result != 0,
3971 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3972
3973 /* undo previous text doesn't reset modify flag */
3974 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3975 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3976 ok (result != 0,
3977 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3978
3979 /* set text with no flag to keep undo stack should not set modify flag */
3980 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3981 setText.flags = 0;
3982 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3983 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3984 ok (result == 0,
3985 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3986
3987 /* WM_SETTEXT doesn't modify */
3988 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3989 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3990 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3991 ok (result == 0,
3992 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3993
3994 /* clear the text */
3995 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3996 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3997 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3998 ok (result == 0,
3999 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4000
4001 /* replace text */
4002 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4003 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4004 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4005 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4006 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4007 ok (result != 0,
4008 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4009
4010 /* copy/paste text 1 */
4011 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4012 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4013 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4014 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4015 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4016 ok (result != 0,
4017 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4018
4019 /* copy/paste text 2 */
4020 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4021 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4022 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4023 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4024 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4025 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4026 ok (result != 0,
4027 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4028
4029 /* press char */
4030 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4031 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4032 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4033 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4034 ok (result != 0,
4035 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4036
4037 /* press del */
4038 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4039 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4040 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4041 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4042 ok (result != 0,
4043 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4044
4045 /* set char format */
4046 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4047 cf2.cbSize = sizeof(CHARFORMAT2);
4048 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4049 (LPARAM) &cf2);
4050 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4051 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4052 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4053 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4054 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4055 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4056 ok (result != 0,
4057 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4058
4059 /* set para format */
4060 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4061 pf2.cbSize = sizeof(PARAFORMAT2);
4062 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4063 (LPARAM) &pf2);
4064 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4065 pf2.wAlignment = PFA_RIGHT;
4066 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4067 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4068 ok (result == 0,
4069 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4070
4071 /* EM_STREAM */
4072 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4073 es.dwCookie = (DWORD_PTR)&streamText;
4074 es.dwError = 0;
4075 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4076 SendMessage(hwndRichEdit, EM_STREAMIN,
4077 (WPARAM)(SF_TEXT), (LPARAM)&es);
4078 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4079 ok (result != 0,
4080 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4081
4082 DestroyWindow(hwndRichEdit);
4083 }
4084
4085 struct exsetsel_s {
4086 long min;
4087 long max;
4088 long expected_retval;
4089 int expected_getsel_start;
4090 int expected_getsel_end;
4091 int _exsetsel_todo_wine;
4092 int _getsel_todo_wine;
4093 };
4094
4095 const struct exsetsel_s exsetsel_tests[] = {
4096 /* sanity tests */
4097 {5, 10, 10, 5, 10, 0, 0},
4098 {15, 17, 17, 15, 17, 0, 0},
4099 /* test cpMax > strlen() */
4100 {0, 100, 18, 0, 18, 0, 1},
4101 /* test cpMin == cpMax */
4102 {5, 5, 5, 5, 5, 0, 0},
4103 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4104 {-1, 0, 5, 5, 5, 0, 0},
4105 {-1, 17, 5, 5, 5, 0, 0},
4106 {-1, 18, 5, 5, 5, 0, 0},
4107 /* test cpMin < 0 && cpMax < 0 */
4108 {-1, -1, 17, 17, 17, 0, 0},
4109 {-4, -5, 17, 17, 17, 0, 0},
4110 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4111 {0, -1, 18, 0, 18, 0, 1},
4112 {17, -5, 18, 17, 18, 0, 1},
4113 {18, -3, 17, 17, 17, 0, 0},
4114 /* test if cpMin > cpMax */
4115 {15, 19, 18, 15, 18, 0, 1},
4116 {19, 15, 18, 15, 18, 0, 1}
4117 };
4118
4119 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4120 CHARRANGE cr;
4121 long result;
4122 int start, end;
4123
4124 cr.cpMin = setsel->min;
4125 cr.cpMax = setsel->max;
4126 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4127
4128 if (setsel->_exsetsel_todo_wine) {
4129 todo_wine {
4130 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4131 }
4132 } else {
4133 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4134 }
4135
4136 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4137
4138 if (setsel->_getsel_todo_wine) {
4139 todo_wine {
4140 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4141 }
4142 } else {
4143 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4144 }
4145 }
4146
4147 static void test_EM_EXSETSEL(void)
4148 {
4149 HWND hwndRichEdit = new_richedit(NULL);
4150 int i;
4151 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4152
4153 /* sending some text to the window */
4154 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4155 /* 01234567890123456*/
4156 /* 10 */
4157
4158 for (i = 0; i < num_tests; i++) {
4159 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4160 }
4161
4162 DestroyWindow(hwndRichEdit);
4163 }
4164
4165 static void test_EM_REPLACESEL(int redraw)
4166 {
4167 HWND hwndRichEdit = new_richedit(NULL);
4168 char buffer[1024] = {0};
4169 int r;
4170 GETTEXTEX getText;
4171 CHARRANGE cr;
4172
4173 /* sending some text to the window */
4174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4175 /* 01234567890123456*/
4176 /* 10 */
4177
4178 /* FIXME add more tests */
4179 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4180 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4181 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4182 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4183 r = strcmp(buffer, "testing");
4184 ok(0 == r, "expected %d, got %d\n", 0, r);
4185
4186 DestroyWindow(hwndRichEdit);
4187
4188 hwndRichEdit = new_richedit(NULL);
4189
4190 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4191 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4192
4193 /* Test behavior with carriage returns and newlines */
4194 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4195 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4196 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4197 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4198 r = strcmp(buffer, "RichEdit1");
4199 ok(0 == r, "expected %d, got %d\n", 0, r);
4200 getText.cb = 1024;
4201 getText.codepage = CP_ACP;
4202 getText.flags = GT_DEFAULT;
4203 getText.lpDefaultChar = NULL;
4204 getText.lpUsedDefChar = NULL;
4205 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4206 ok(strcmp(buffer, "RichEdit1") == 0,
4207 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4208
4209 /* Test number of lines reported after EM_REPLACESEL */
4210 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4211 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4212
4213 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4214 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4215 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4216 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4217 r = strcmp(buffer, "RichEdit1\r\n");
4218 ok(0 == r, "expected %d, got %d\n", 0, r);
4219 getText.cb = 1024;
4220 getText.codepage = CP_ACP;
4221 getText.flags = GT_DEFAULT;
4222 getText.lpDefaultChar = NULL;
4223 getText.lpUsedDefChar = NULL;
4224 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4225 ok(strcmp(buffer, "RichEdit1\r") == 0,
4226 "EM_GETTEXTEX returned incorrect string\n");
4227
4228 /* Test number of lines reported after EM_REPLACESEL */
4229 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4230 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4231
4232 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4233 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4234 returns the number of characters *inserted* into the control (after
4235 required conversions), but WinXP's riched20 returns the number of
4236 characters interpreted from the original lParam. Wine's builtin riched20
4237 implements the WinXP behavior.
4238 */
4239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4240 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4241 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4242 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4243
4244 /* Test number of lines reported after EM_REPLACESEL */
4245 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4246 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4247
4248 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4249 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4250 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4251 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4252
4253 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4254 r = strcmp(buffer, "RichEdit1\r\n");
4255 ok(0 == r, "expected %d, got %d\n", 0, r);
4256 getText.cb = 1024;
4257 getText.codepage = CP_ACP;
4258 getText.flags = GT_DEFAULT;
4259 getText.lpDefaultChar = NULL;
4260 getText.lpUsedDefChar = NULL;
4261 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4262 ok(strcmp(buffer, "RichEdit1\r") == 0,
4263 "EM_GETTEXTEX returned incorrect string\n");
4264
4265 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4266 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4267 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4268 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4269
4270 /* The following tests show that richedit should handle the special \r\r\n
4271 sequence by turning it into a single space on insertion. However,
4272 EM_REPLACESEL on WinXP returns the number of characters in the original
4273 string.
4274 */
4275
4276 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4277 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4278 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4279 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4280 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4281 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4282 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4283
4284 /* Test the actual string */
4285 getText.cb = 1024;
4286 getText.codepage = CP_ACP;
4287 getText.flags = GT_DEFAULT;
4288 getText.lpDefaultChar = NULL;
4289 getText.lpUsedDefChar = NULL;
4290 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4291 ok(strcmp(buffer, "\r\r") == 0,
4292 "EM_GETTEXTEX returned incorrect string\n");
4293
4294 /* Test number of lines reported after EM_REPLACESEL */
4295 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4296 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4297
4298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4299 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4300 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4301 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4302 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4303 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4304 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4305 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4306
4307 /* Test the actual string */
4308 getText.cb = 1024;
4309 getText.codepage = CP_ACP;
4310 getText.flags = GT_DEFAULT;
4311 getText.lpDefaultChar = NULL;
4312 getText.lpUsedDefChar = NULL;
4313 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4314 ok(strcmp(buffer, " ") == 0,
4315 "EM_GETTEXTEX returned incorrect string\n");
4316
4317 /* Test number of lines reported after EM_REPLACESEL */
4318 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4319 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4320
4321 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4322 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4323 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4324 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4325 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4326 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4327 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4328 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4329
4330 /* Test the actual string */
4331 getText.cb = 1024;
4332 getText.codepage = CP_ACP;
4333 getText.flags = GT_DEFAULT;
4334 getText.lpDefaultChar = NULL;
4335 getText.lpUsedDefChar = NULL;
4336 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4337 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4338 "EM_GETTEXTEX returned incorrect string\n");
4339
4340 /* Test number of lines reported after EM_REPLACESEL */
4341 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4342 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4343
4344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4345 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4346 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4347 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4348 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4349 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4350 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4351 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4352
4353 /* Test the actual string */
4354 getText.cb = 1024;
4355 getText.codepage = CP_ACP;
4356 getText.flags = GT_DEFAULT;
4357 getText.lpDefaultChar = NULL;
4358 getText.lpUsedDefChar = NULL;
4359 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4360 ok(strcmp(buffer, " \r") == 0,
4361 "EM_GETTEXTEX returned incorrect string\n");
4362
4363 /* Test number of lines reported after EM_REPLACESEL */
4364 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4365 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4366
4367 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4368 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4369 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4370 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4371 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4372 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4373 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4374 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4375
4376 /* Test the actual string */
4377 getText.cb = 1024;
4378 getText.codepage = CP_ACP;
4379 getText.flags = GT_DEFAULT;
4380 getText.lpDefaultChar = NULL;
4381 getText.lpUsedDefChar = NULL;
4382 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4383 ok(strcmp(buffer, " \r\r") == 0,
4384 "EM_GETTEXTEX returned incorrect string\n");
4385
4386 /* Test number of lines reported after EM_REPLACESEL */
4387 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4388 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4389
4390 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4391 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4392 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4393 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4394 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4395 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4396 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4397 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4398
4399 /* Test the actual string */
4400 getText.cb = 1024;
4401 getText.codepage = CP_ACP;
4402 getText.flags = GT_DEFAULT;
4403 getText.lpDefaultChar = NULL;
4404 getText.lpUsedDefChar = NULL;
4405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4406 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4407 "EM_GETTEXTEX returned incorrect string\n");
4408
4409 /* Test number of lines reported after EM_REPLACESEL */
4410 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4411 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4412
4413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4414 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4415 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4416 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4417 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4418 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4419 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4420
4421 /* Test the actual string */
4422 getText.cb = 1024;
4423 getText.codepage = CP_ACP;
4424 getText.flags = GT_DEFAULT;
4425 getText.lpDefaultChar = NULL;
4426 getText.lpUsedDefChar = NULL;
4427 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4428 ok(strcmp(buffer, "\r\r") == 0,
4429 "EM_GETTEXTEX returned incorrect string\n");
4430
4431 /* Test number of lines reported after EM_REPLACESEL */
4432 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4433 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4434
4435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4436 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4437 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4438 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4439 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4440 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4441 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4442 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4443
4444 /* Test the actual string */
4445 getText.cb = 1024;
4446 getText.codepage = CP_ACP;
4447 getText.flags = GT_DEFAULT;
4448 getText.lpDefaultChar = NULL;
4449 getText.lpUsedDefChar = NULL;
4450 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4451 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4452 "EM_GETTEXTEX returned incorrect string\n");
4453
4454 /* Test number of lines reported after EM_REPLACESEL */
4455 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4456 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4457
4458 if (!redraw)
4459 /* This is needed to avoid interferring with keybd_event calls
4460 * on other tests that simulate keyboard events. */
4461 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4462
4463 DestroyWindow(hwndRichEdit);
4464 }
4465
4466 static void test_WM_PASTE(void)
4467 {
4468 int result;
4469 char buffer[1024] = {0};
4470 const char* text1 = "testing paste\r";
4471 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4472 const char* text1_after = "testing paste\r\n";
4473 const char* text2 = "testing paste\r\rtesting paste";
4474 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4475 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4476 HWND hwndRichEdit = new_richedit(NULL);
4477
4478 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4479 * to test the state of the modifiers (Ctrl/Alt/Shift).
4480 *
4481 * Therefore Ctrl-<key> keystrokes need to be simulated with
4482 * keybd_event or by using SetKeyboardState to set the modifiers
4483 * and SendMessage to simulate the keystrokes.
4484 */
4485
4486 /* Sent keystrokes with keybd_event */
4487 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4488 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4489 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4490 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4491 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4492
4493 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4494 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4495
4496 SEND_CTRL_C(hwndRichEdit); /* Copy */
4497 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4498 SEND_CTRL_V(hwndRichEdit); /* Paste */
4499 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4500 /* Pasted text should be visible at this step */
4501 result = strcmp(text1_step1, buffer);
4502 ok(result == 0,
4503 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4504
4505 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4506 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4507 /* Text should be the same as before (except for \r -> \r\n conversion) */
4508 result = strcmp(text1_after, buffer);
4509 ok(result == 0,
4510 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4511
4512 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4513 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4514 SEND_CTRL_C(hwndRichEdit); /* Copy */
4515 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4516 SEND_CTRL_V(hwndRichEdit); /* Paste */
4517 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4518 /* Pasted text should be visible at this step */
4519 result = strcmp(text3, buffer);
4520 ok(result == 0,
4521 "test paste: strcmp = %i\n", result);
4522 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4523 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4524 /* Text should be the same as before (except for \r -> \r\n conversion) */
4525 result = strcmp(text2_after, buffer);
4526 ok(result == 0,
4527 "test paste: strcmp = %i\n", result);
4528 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4529 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4530 /* Text should revert to post-paste state */
4531 result = strcmp(buffer,text3);
4532 ok(result == 0,
4533 "test paste: strcmp = %i\n", result);
4534
4535 #undef SEND_CTRL_C
4536 #undef SEND_CTRL_X
4537 #undef SEND_CTRL_V
4538 #undef SEND_CTRL_Z
4539 #undef SEND_CTRL_Y
4540
4541 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4542 /* Send WM_CHAR to simulates Ctrl-V */
4543 SendMessage(hwndRichEdit, WM_CHAR, 22,
4544 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4545 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4546 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4547 result = strcmp(buffer,"");
4548 ok(result == 0,
4549 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4550
4551 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4552 * with SetKeyboard state. */
4553
4554 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4555 /* Simulates paste (Ctrl-V) */
4556 hold_key(VK_CONTROL);
4557 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4558 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4559 release_key(VK_CONTROL);
4560 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4561 result = strcmp(buffer,"paste");
4562 ok(result == 0,
4563 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4564
4565 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4566 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4567 /* Simulates copy (Ctrl-C) */
4568 hold_key(VK_CONTROL);
4569 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4570 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4571 release_key(VK_CONTROL);
4572 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4573 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4574 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4575 result = strcmp(buffer,"testing");
4576 ok(result == 0,
4577 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4578
4579 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4580 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4581 /* Simulates select all (Ctrl-A) */
4582 hold_key(VK_CONTROL);
4583 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4584 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4585 /* Simulates select cut (Ctrl-X) */
4586 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4587 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4588 release_key(VK_CONTROL);
4589 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4590 result = strcmp(buffer,"");
4591 ok(result == 0,
4592 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4594 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4595 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4596 result = strcmp(buffer,"cut\r\n");
4597 todo_wine ok(result == 0,
4598 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4599 /* Simulates undo (Ctrl-Z) */
4600 hold_key(VK_CONTROL);
4601 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4602 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4603 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4604 result = strcmp(buffer,"");
4605 ok(result == 0,
4606 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4607 /* Simulates redo (Ctrl-Y) */
4608 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4609 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4610 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4611 result = strcmp(buffer,"cut\r\n");
4612 todo_wine ok(result == 0,
4613 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4614 release_key(VK_CONTROL);
4615
4616 DestroyWindow(hwndRichEdit);
4617 }
4618
4619 static void test_EM_FORMATRANGE(void)
4620 {
4621 int r;
4622 FORMATRANGE fr;
4623 HDC hdc;
4624 HWND hwndRichEdit = new_richedit(NULL);
4625
4626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4627
4628 hdc = GetDC(hwndRichEdit);
4629 ok(hdc != NULL, "Could not get HDC\n");
4630
4631 fr.hdc = fr.hdcTarget = hdc;
4632 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4633 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4634 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4635 fr.chrg.cpMin = 0;
4636 fr.chrg.cpMax = 20;
4637
4638 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4639 todo_wine {
4640 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4641 }
4642
4643 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4644 todo_wine {
4645 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4646 }
4647
4648 fr.chrg.cpMin = 0;
4649 fr.chrg.cpMax = 10;
4650
4651 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4652 todo_wine {
4653 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4654 }
4655
4656 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4657 todo_wine {
4658 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4659 }
4660
4661 DestroyWindow(hwndRichEdit);
4662 }
4663
4664 static int nCallbackCount = 0;
4665
4666 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4667 LONG cb, LONG* pcb)
4668 {
4669 const char text[] = {'t','e','s','t'};
4670
4671 if (sizeof(text) <= cb)
4672 {
4673 if ((int)dwCookie != nCallbackCount)
4674 {
4675 *pcb = 0;
4676 return 0;
4677 }
4678
4679 memcpy (pbBuff, text, sizeof(text));
4680 *pcb = sizeof(text);
4681
4682 nCallbackCount++;
4683
4684 return 0;
4685 }
4686 else
4687 return 1; /* indicates callback failed */
4688 }
4689
4690 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4691 LPBYTE pbBuff,
4692 LONG cb,
4693 LONG *pcb)
4694 {
4695 const char** str = (const char**)dwCookie;
4696 int size = strlen(*str);
4697 *pcb = cb;
4698 if (*pcb > size) {
4699 *pcb = size;
4700 }
4701 if (*pcb > 0) {
4702 memcpy(pbBuff, *str, *pcb);
4703 *str += *pcb;
4704 }
4705 return 0;
4706 }
4707
4708 struct StringWithLength {
4709 int length;
4710 char *buffer;
4711 };
4712
4713 /* This callback is used to handled the null characters in a string. */
4714 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4715 LPBYTE pbBuff,
4716 LONG cb,
4717 LONG *pcb)
4718 {
4719 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4720 int size = str->length;
4721 *pcb = cb;
4722 if (*pcb > size) {
4723 *pcb = size;
4724 }
4725 if (*pcb > 0) {
4726 memcpy(pbBuff, str->buffer, *pcb);
4727 str->buffer += *pcb;
4728 str->length -= *pcb;
4729 }
4730 return 0;
4731 }
4732
4733 static void test_EM_STREAMIN(void)
4734 {
4735 HWND hwndRichEdit = new_richedit(NULL);
4736 LRESULT result;
4737 EDITSTREAM es;
4738 char buffer[1024] = {0};
4739
4740 const char * streamText0 = "{\\rtf1 TestSomeText}";
4741 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4742 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4743
4744 const char * streamText1 =
4745 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4746 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4747 "}\r\n";
4748
4749 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4750 const char * streamText2 =
4751 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4752 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4753 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4754 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4755 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4756 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4757 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4758
4759 const char * streamText3 = "RichEdit1";
4760
4761 struct StringWithLength cookieForStream4;
4762 const char * streamText4 =
4763 "This text just needs to be long enough to cause run to be split onto "
4764 "two separate lines and make sure the null terminating character is "
4765 "handled properly.\0";
4766 int length4 = strlen(streamText4) + 1;
4767 cookieForStream4.buffer = (char *)streamText4;
4768 cookieForStream4.length = length4;
4769
4770 /* Minimal test without \par at the end */
4771 es.dwCookie = (DWORD_PTR)&streamText0;
4772 es.dwError = 0;
4773 es.pfnCallback = test_EM_STREAMIN_esCallback;
4774 SendMessage(hwndRichEdit, EM_STREAMIN,
4775 (WPARAM)(SF_RTF), (LPARAM)&es);
4776
4777 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4778 ok (result == 12,
4779 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4780 result = strcmp (buffer,"TestSomeText");
4781 ok (result == 0,
4782 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4783 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4784
4785 /* Native richedit 2.0 ignores last \par */
4786 es.dwCookie = (DWORD_PTR)&streamText0a;
4787 es.dwError = 0;
4788 es.pfnCallback = test_EM_STREAMIN_esCallback;
4789 SendMessage(hwndRichEdit, EM_STREAMIN,
4790 (WPARAM)(SF_RTF), (LPARAM)&es);
4791
4792 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4793 ok (result == 12,
4794 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4795 result = strcmp (buffer,"TestSomeText");
4796 ok (result == 0,
4797 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4798 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4799
4800 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4801 es.dwCookie = (DWORD_PTR)&streamText0b;
4802 es.dwError = 0;
4803 es.pfnCallback = test_EM_STREAMIN_esCallback;
4804 SendMessage(hwndRichEdit, EM_STREAMIN,
4805 (WPARAM)(SF_RTF), (LPARAM)&es);
4806
4807 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4808 ok (result == 14,
4809 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4810 result = strcmp (buffer,"TestSomeText\r\n");
4811 ok (result == 0,
4812 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4813 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4814
4815 es.dwCookie = (DWORD_PTR)&streamText1;
4816 es.dwError = 0;
4817 es.pfnCallback = test_EM_STREAMIN_esCallback;
4818 SendMessage(hwndRichEdit, EM_STREAMIN,
4819 (WPARAM)(SF_RTF), (LPARAM)&es);
4820
4821 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4822 ok (result == 12,
4823 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4824 result = strcmp (buffer,"TestSomeText");
4825 ok (result == 0,
4826 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4827 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4828
4829 es.dwCookie = (DWORD_PTR)&streamText2;
4830 es.dwError = 0;
4831 SendMessage(hwndRichEdit, EM_STREAMIN,
4832 (WPARAM)(SF_RTF), (LPARAM)&es);
4833
4834 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4835 ok (result == 0,
4836 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4837 ok (strlen(buffer) == 0,
4838 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4839 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4840
4841 es.dwCookie = (DWORD_PTR)&streamText3;
4842 es.dwError = 0;
4843 SendMessage(hwndRichEdit, EM_STREAMIN,
4844 (WPARAM)(SF_RTF), (LPARAM)&es);
4845
4846 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4847 ok (result == 0,
4848 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4849 ok (strlen(buffer) == 0,
4850 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4851 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4852
4853 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4854 es.dwError = 0;
4855 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4856 SendMessage(hwndRichEdit, EM_STREAMIN,
4857 (WPARAM)(SF_TEXT), (LPARAM)&es);
4858
4859 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4860 ok (result == length4,
4861 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4862 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4863
4864 DestroyWindow(hwndRichEdit);
4865 }
4866
4867 static void test_EM_StreamIn_Undo(void)
4868 {
4869 /* The purpose of this test is to determine when a EM_StreamIn should be
4870 * undoable. This is important because WM_PASTE currently uses StreamIn and
4871 * pasting should always be undoable but streaming isn't always.
4872 *
4873 * cases to test:
4874 * StreamIn plain text without SFF_SELECTION.
4875 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4876 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4877 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4878 * Feel free to add tests for other text modes or StreamIn things.
4879 */
4880
4881
4882 HWND hwndRichEdit = new_richedit(NULL);
4883 LRESULT result;
4884 EDITSTREAM es;
4885 char buffer[1024] = {0};
4886 const char randomtext[] = "Some text";
4887
4888 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4889
4890 /* StreamIn, no SFF_SELECTION */
4891 es.dwCookie = nCallbackCount;
4892 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4893 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4894 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4895 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4896 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4897 result = strcmp (buffer,"test");
4898 ok (result == 0,
4899 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4900
4901 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4902 ok (result == FALSE,
4903 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4904
4905 /* StreamIn, SFF_SELECTION, but nothing selected */
4906 es.dwCookie = nCallbackCount;
4907 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4908 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4909 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4910 SendMessage(hwndRichEdit, EM_STREAMIN,
4911 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4912 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4913 result = strcmp (buffer,"testSome text");
4914 ok (result == 0,
4915 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4916
4917 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4918 ok (result == TRUE,
4919 "EM_STREAMIN with SFF_SELECTION but no selection set "
4920 "should create an undo\n");
4921
4922 /* StreamIn, SFF_SELECTION, with a selection */
4923 es.dwCookie = nCallbackCount;
4924 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4925 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4926 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4927 SendMessage(hwndRichEdit, EM_STREAMIN,
4928 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4929 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4930 result = strcmp (buffer,"Sometesttext");
4931 ok (result == 0,
4932 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4933
4934 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4935 ok (result == TRUE,
4936 "EM_STREAMIN with SFF_SELECTION and selection set "
4937 "should create an undo\n");
4938
4939 DestroyWindow(hwndRichEdit);
4940 }
4941
4942 static BOOL is_em_settextex_supported(HWND hwnd)
4943 {
4944 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4945 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4946 }
4947
4948 static void test_unicode_conversions(void)
4949 {
4950 static const WCHAR tW[] = {'t',0};
4951 static const WCHAR teW[] = {'t','e',0};
4952 static const WCHAR textW[] = {'t','e','s','t',0};
4953 static const char textA[] = "test";
4954 char bufA[64];
4955 WCHAR bufW[64];
4956 HWND hwnd;
4957 int em_settextex_supported, ret;
4958
4959 #define set_textA(hwnd, wm_set_text, txt) \
4960 do { \
4961 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4962 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4963 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4964 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4965 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4966 } while(0)
4967 #define expect_textA(hwnd, wm_get_text, txt) \
4968 do { \
4969 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4970 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4971 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4972 memset(bufA, 0xAA, sizeof(bufA)); \
4973 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4974 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4975 ret = lstrcmpA(bufA, txt); \
4976 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4977 } while(0)
4978
4979 #define set_textW(hwnd, wm_set_text, txt) \
4980 do { \
4981 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4982 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4983 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4984 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4985 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4986 } while(0)
4987 #define expect_textW(hwnd, wm_get_text, txt) \
4988 do { \
4989 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4990 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4991 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4992 memset(bufW, 0xAA, sizeof(bufW)); \
4993 if (is_win9x) \
4994 { \
4995 assert(wm_get_text == EM_GETTEXTEX); \
4996 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4997 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4998 } \
4999 else \
5000 { \
5001 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5002 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5003 } \
5004 ret = lstrcmpW(bufW, txt); \
5005 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5006 } while(0)
5007 #define expect_empty(hwnd, wm_get_text) \
5008 do { \
5009 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5010 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5011 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5012 memset(bufA, 0xAA, sizeof(bufA)); \
5013 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5014 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5015 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5016 } while(0)
5017
5018 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5019 0, 0, 200, 60, 0, 0, 0, 0);
5020 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5021
5022 ret = IsWindowUnicode(hwnd);
5023 if (is_win9x)
5024 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5025 else
5026 ok(ret, "RichEdit20W should be unicode under NT\n");
5027
5028 /* EM_SETTEXTEX is supported starting from version 3.0 */
5029 em_settextex_supported = is_em_settextex_supported(hwnd);
5030 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5031 em_settextex_supported ? "" : "NOT ");
5032
5033 expect_empty(hwnd, WM_GETTEXT);
5034 expect_empty(hwnd, EM_GETTEXTEX);
5035
5036 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5037 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5038 expect_textA(hwnd, WM_GETTEXT, "t");
5039 expect_textA(hwnd, EM_GETTEXTEX, "t");
5040 expect_textW(hwnd, EM_GETTEXTEX, tW);
5041
5042 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5043 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5044 expect_textA(hwnd, WM_GETTEXT, "te");
5045 expect_textA(hwnd, EM_GETTEXTEX, "te");
5046 expect_textW(hwnd, EM_GETTEXTEX, teW);
5047
5048 set_textA(hwnd, WM_SETTEXT, NULL);
5049 expect_empty(hwnd, WM_GETTEXT);
5050 expect_empty(hwnd, EM_GETTEXTEX);
5051
5052 if (is_win9x)
5053 set_textA(hwnd, WM_SETTEXT, textW);
5054 else
5055 set_textA(hwnd, WM_SETTEXT, textA);
5056 expect_textA(hwnd, WM_GETTEXT, textA);
5057 expect_textA(hwnd, EM_GETTEXTEX, textA);
5058 expect_textW(hwnd, EM_GETTEXTEX, textW);
5059
5060 if (em_settextex_supported)
5061 {
5062 set_textA(hwnd, EM_SETTEXTEX, textA);
5063 expect_textA(hwnd, WM_GETTEXT, textA);
5064 expect_textA(hwnd, EM_GETTEXTEX, textA);
5065 expect_textW(hwnd, EM_GETTEXTEX, textW);
5066 }
5067
5068 if (!is_win9x)
5069 {
5070 set_textW(hwnd, WM_SETTEXT, textW);
5071 expect_textW(hwnd, WM_GETTEXT, textW);
5072 expect_textA(hwnd, WM_GETTEXT, textA);
5073 expect_textW(hwnd, EM_GETTEXTEX, textW);
5074 expect_textA(hwnd, EM_GETTEXTEX, textA);
5075
5076 if (em_settextex_supported)
5077 {
5078 set_textW(hwnd, EM_SETTEXTEX, textW);
5079 expect_textW(hwnd, WM_GETTEXT, textW);
5080 expect_textA(hwnd, WM_GETTEXT, textA);
5081 expect_textW(hwnd, EM_GETTEXTEX, textW);
5082 expect_textA(hwnd, EM_GETTEXTEX, textA);
5083 }
5084 }
5085 DestroyWindow(hwnd);
5086
5087 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5088 0, 0, 200, 60, 0, 0, 0, 0);
5089 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5090
5091 ret = IsWindowUnicode(hwnd);
5092 ok(!ret, "RichEdit20A should NOT be unicode\n");
5093
5094 set_textA(hwnd, WM_SETTEXT, textA);
5095 expect_textA(hwnd, WM_GETTEXT, textA);
5096 expect_textA(hwnd, EM_GETTEXTEX, textA);
5097 expect_textW(hwnd, EM_GETTEXTEX, textW);
5098
5099 if (em_settextex_supported)
5100 {
5101 set_textA(hwnd, EM_SETTEXTEX, textA);
5102 expect_textA(hwnd, WM_GETTEXT, textA);
5103 expect_textA(hwnd, EM_GETTEXTEX, textA);
5104 expect_textW(hwnd, EM_GETTEXTEX, textW);
5105 }
5106
5107 if (!is_win9x)
5108 {
5109 set_textW(hwnd, WM_SETTEXT, textW);
5110 expect_textW(hwnd, WM_GETTEXT, textW);
5111 expect_textA(hwnd, WM_GETTEXT, textA);
5112 expect_textW(hwnd, EM_GETTEXTEX, textW);
5113 expect_textA(hwnd, EM_GETTEXTEX, textA);
5114
5115 if (em_settextex_supported)
5116 {
5117 set_textW(hwnd, EM_SETTEXTEX, textW);
5118 expect_textW(hwnd, WM_GETTEXT, textW);
5119 expect_textA(hwnd, WM_GETTEXT, textA);
5120 expect_textW(hwnd, EM_GETTEXTEX, textW);
5121 expect_textA(hwnd, EM_GETTEXTEX, textA);
5122 }
5123 }
5124 DestroyWindow(hwnd);
5125 }
5126
5127 static void test_WM_CHAR(void)
5128 {
5129 HWND hwnd;
5130 int ret;
5131 const char * char_list = "abc\rabc\r";
5132 const char * expected_content_single = "abcabc";
5133 const char * expected_content_multi = "abc\r\nabc\r\n";
5134 char buffer[64] = {0};
5135 const char * p;
5136
5137 /* single-line control must IGNORE carriage returns */
5138 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5139 0, 0, 200, 60, 0, 0, 0, 0);
5140 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5141
5142 p = char_list;
5143 while (*p != '\0') {
5144 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5145 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5146 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5147 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5148 p++;
5149 }
5150
5151 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5152 ret = strcmp(buffer, expected_content_single);
5153 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5154
5155 DestroyWindow(hwnd);
5156
5157 /* multi-line control inserts CR normally */
5158 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5159 0, 0, 200, 60, 0, 0, 0, 0);
5160 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5161
5162 p = char_list;
5163 while (*p != '\0') {
5164 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5165 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5166 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5167 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5168 p++;
5169 }
5170
5171 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5172 ret = strcmp(buffer, expected_content_multi);
5173 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5174
5175 DestroyWindow(hwnd);
5176 }
5177
5178 static void test_EM_GETTEXTLENGTHEX(void)
5179 {
5180 HWND hwnd;
5181 GETTEXTLENGTHEX gtl;
5182 int ret;
5183 const char * base_string = "base string";
5184 const char * test_string = "a\nb\n\n\r\n";
5185 const char * test_string_after = "a";
5186 const char * test_string_2 = "a\rtest\rstring";
5187 char buffer[64] = {0};
5188
5189 /* single line */
5190 if (!is_win9x)
5191 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5192 0, 0, 200, 60, 0, 0, 0, 0);
5193 else
5194 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5195 0, 0, 200, 60, 0, 0, 0, 0);
5196 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5197
5198 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5199 gtl.codepage = CP_ACP;
5200 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5201 ok(ret == 0, "ret %d\n",ret);
5202
5203 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5204 gtl.codepage = CP_ACP;
5205 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5206 ok(ret == 0, "ret %d\n",ret);
5207
5208 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5209
5210 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5211 gtl.codepage = CP_ACP;
5212 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5213 ok(ret == strlen(base_string), "ret %d\n",ret);
5214
5215 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5216 gtl.codepage = CP_ACP;
5217 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5218 ok(ret == strlen(base_string), "ret %d\n",ret);
5219
5220 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5221
5222 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5223 gtl.codepage = CP_ACP;
5224 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5225 ok(ret == 1, "ret %d\n",ret);
5226
5227 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5228 gtl.codepage = CP_ACP;
5229 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5230 ok(ret == 1, "ret %d\n",ret);
5231
5232 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5233 ret = strcmp(buffer, test_string_after);
5234 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5235
5236 DestroyWindow(hwnd);
5237
5238 /* multi line */
5239 if (!is_win9x)
5240 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5241 0, 0, 200, 60, 0, 0, 0, 0);
5242 else
5243 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5244 0, 0, 200, 60, 0, 0, 0, 0);
5245 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5246
5247 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5248 gtl.codepage = CP_ACP;
5249 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5250 ok(ret == 0, "ret %d\n",ret);
5251
5252 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5253 gtl.codepage = CP_ACP;
5254 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5255 ok(ret == 0, "ret %d\n",ret);
5256
5257 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5258
5259 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5260 gtl.codepage = CP_ACP;
5261 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5262 ok(ret == strlen(base_string), "ret %d\n",ret);
5263
5264 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5265 gtl.codepage = CP_ACP;
5266 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5267 ok(ret == strlen(base_string), "ret %d\n",ret);
5268
5269 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5270
5271 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5272 gtl.codepage = CP_ACP;
5273 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5274 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5275
5276 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5277 gtl.codepage = CP_ACP;
5278 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5279 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5280
5281 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5282
5283 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5284 gtl.codepage = CP_ACP;
5285 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5286 ok(ret == 10, "ret %d\n",ret);
5287
5288 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5289 gtl.codepage = CP_ACP;
5290 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5291 ok(ret == 6, "ret %d\n",ret);
5292
5293 DestroyWindow(hwnd);
5294 }
5295
5296
5297 /* globals that parent and child access when checking event masks & notifications */
5298 static HWND eventMaskEditHwnd = 0;
5299 static int queriedEventMask;
5300 static int watchForEventMask = 0;
5301
5302 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5303 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5304 {
5305 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5306 {
5307 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5308 }
5309 return DefWindowProcA(hwnd, message, wParam, lParam);
5310 }
5311
5312 /* test event masks in combination with WM_COMMAND */
5313 static void test_eventMask(void)
5314 {
5315 HWND parent;
5316 int ret, style;
5317 WNDCLASSA cls;
5318 const char text[] = "foo bar\n";
5319 int eventMask;
5320
5321 /* register class to capture WM_COMMAND */
5322 cls.style = 0;
5323 cls.lpfnWndProc = ParentMsgCheckProcA;
5324 cls.cbClsExtra = 0;
5325 cls.cbWndExtra = 0;
5326 cls.hInstance = GetModuleHandleA(0);
5327 cls.hIcon = 0;
5328 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5329 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5330 cls.lpszMenuName = NULL;
5331 cls.lpszClassName = "EventMaskParentClass";
5332 if(!RegisterClassA(&cls)) assert(0);
5333
5334 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5335 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5336 ok (parent != 0, "Failed to create parent window\n");
5337
5338 eventMaskEditHwnd = new_richedit(parent);
5339 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5340
5341 eventMask = ENM_CHANGE | ENM_UPDATE;
5342 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5343 ok(ret == ENM_NONE, "wrong event mask\n");
5344 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5345 ok(ret == eventMask, "failed to set event mask\n");
5346
5347 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5348 queriedEventMask = 0; /* initialize to something other than we expect */
5349 watchForEventMask = EN_CHANGE;
5350 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5351 ok(ret == TRUE, "failed to set text\n");
5352 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5353 notification in response to WM_SETTEXT */
5354 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5355 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5356
5357 /* check to see if EN_CHANGE is sent when redraw is turned off */
5358 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5359 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5360 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5361 /* redraw is disabled by making the window invisible. */
5362 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5363 queriedEventMask = 0; /* initialize to something other than we expect */
5364 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5365 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5366 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5367 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5368 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5369
5370 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5371 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5372 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5373 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5374 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5375 watchForEventMask = EN_UPDATE;
5376 queriedEventMask = 0; /* initialize to something other than we expect */
5377 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5378 ok(queriedEventMask == 0,
5379 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5380 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5381 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5382 queriedEventMask = 0; /* initialize to something other than we expect */
5383 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5384 ok(queriedEventMask == eventMask,
5385 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5386
5387
5388 DestroyWindow(parent);
5389 }
5390
5391 static int received_WM_NOTIFY = 0;
5392 static int modify_at_WM_NOTIFY = 0;
5393 static HWND hwndRichedit_WM_NOTIFY;
5394
5395 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5396 {
5397 if(message == WM_NOTIFY)
5398 {
5399 received_WM_NOTIFY = 1;
5400 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5401 }
5402 return DefWindowProcA(hwnd, message, wParam, lParam);
5403 }
5404
5405 static void test_WM_NOTIFY(void)
5406 {
5407 HWND parent;
5408 WNDCLASSA cls;
5409 CHARFORMAT2 cf2;
5410
5411 /* register class to capture WM_NOTIFY */
5412 cls.style = 0;
5413 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5414 cls.cbClsExtra = 0;
5415 cls.cbWndExtra = 0;
5416 cls.hInstance = GetModuleHandleA(0);
5417 cls.hIcon = 0;
5418 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5419 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5420 cls.lpszMenuName = NULL;
5421 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5422 if(!RegisterClassA(&cls)) assert(0);
5423
5424 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5425 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5426 ok (parent != 0, "Failed to create parent window\n");
5427
5428 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5429 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5430
5431 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5432
5433 /* Notifications for selection change should only be sent when selection
5434 actually changes. EM_SETCHARFORMAT is one message that calls
5435 ME_CommitUndo, which should check whether message should be sent */
5436 received_WM_NOTIFY = 0;
5437 cf2.cbSize = sizeof(CHARFORMAT2);
5438 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5439 (LPARAM) &cf2);
5440 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5441 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5442 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5443 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5444
5445 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5446 already at 0. */
5447 received_WM_NOTIFY = 0;
5448 modify_at_WM_NOTIFY = 0;
5449 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5450 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5451 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5452
5453 received_WM_NOTIFY = 0;
5454 modify_at_WM_NOTIFY = 0;
5455 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5456 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5457
5458 received_WM_NOTIFY = 0;
5459 modify_at_WM_NOTIFY = 0;
5460 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5461 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5462 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5463
5464 /* Test for WM_NOTIFY messages with redraw disabled. */
5465 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5466 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5467 received_WM_NOTIFY = 0;
5468 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5469 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5470 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5471
5472 DestroyWindow(hwndRichedit_WM_NOTIFY);
5473 DestroyWindow(parent);
5474 }
5475
5476 static void test_undo_coalescing(void)
5477 {
5478 HWND hwnd;
5479 int result;
5480 char buffer[64] = {0};
5481
5482 /* multi-line control inserts CR normally */
5483 if (!is_win9x)
5484 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5485 0, 0, 200, 60, 0, 0, 0, 0);
5486 else
5487 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5488 0, 0, 200, 60, 0, 0, 0, 0);
5489 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5490
5491 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5492 ok (result == FALSE, "Can undo after window creation.\n");
5493 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5494 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5495 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5496 ok (result == FALSE, "Can redo after window creation.\n");
5497 result = SendMessage(hwnd, EM_REDO, 0, 0);
5498 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5499
5500 /* Test the effect of arrows keys during typing on undo transactions*/
5501 simulate_typing_characters(hwnd, "one two three");
5502 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5503 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5504 simulate_typing_characters(hwnd, " four five six");
5505
5506 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5507 ok (result == FALSE, "Can redo before anything is undone.\n");
5508 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5509 ok (result == TRUE, "Cannot undo typed characters.\n");
5510 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5511 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5512 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5513 ok (result == TRUE, "Cannot redo after undo.\n");
5514 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5515 result = strcmp(buffer, "one two three");
5516 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5517
5518 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5519 ok (result == TRUE, "Cannot undo typed characters.\n");
5520 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5521 ok (result == TRUE, "Failed to undo typed characters.\n");
5522 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5523 result = strcmp(buffer, "");
5524 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5525
5526 /* Test the effect of focus changes during typing on undo transactions*/
5527 simulate_typing_characters(hwnd, "one two three");
5528 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5529 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5530 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5531 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5532 simulate_typing_characters(hwnd, " four five six");
5533 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5534 ok (result == TRUE, "Failed to undo typed characters.\n");
5535 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5536 result = strcmp(buffer, "one two three");
5537 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5538
5539 /* Test the effect of the back key during typing on undo transactions */
5540 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5541 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5542 ok (result == TRUE, "Failed to clear the text.\n");
5543 simulate_typing_characters(hwnd, "one two threa");
5544 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5545 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5546 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5547 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5548 simulate_typing_characters(hwnd, "e four five six");
5549 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5550 ok (result == TRUE, "Failed to undo typed characters.\n");
5551 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5552 result = strcmp(buffer, "");
5553 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5554
5555 /* Test the effect of the delete key during typing on undo transactions */
5556 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5557 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5558 ok(result == TRUE, "Failed to set the text.\n");
5559 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5560 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5561 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5562 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5563 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5564 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5565 ok (result == TRUE, "Failed to undo typed characters.\n");
5566 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5567 result = strcmp(buffer, "acd");
5568 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5569 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5570 ok (result == TRUE, "Failed to undo typed characters.\n");
5571 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5572 result = strcmp(buffer, "abcd");
5573 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5574
5575 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5576 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5577 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5578 ok (result == TRUE, "Failed to clear the text.\n");
5579 simulate_typing_characters(hwnd, "one two three");
5580 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5581 ok (result == 0, "expected %d but got %d\n", 0, result);
5582 simulate_typing_characters(hwnd, " four five six");
5583 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5584 ok (result == TRUE, "Failed to undo typed characters.\n");
5585 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5586 result = strcmp(buffer, "one two three");
5587 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5588 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5589 ok (result == TRUE, "Failed to undo typed characters.\n");
5590 ok (result == TRUE, "Failed to undo typed characters.\n");
5591 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5592 result = strcmp(buffer, "");
5593 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5594
5595 DestroyWindow(hwnd);
5596 }
5597
5598 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5599 {
5600 int length;
5601
5602 /* MSDN lied, length is actually the number of bytes. */
5603 length = bytes / sizeof(WCHAR);
5604 switch(code)
5605 {
5606 case WB_ISDELIMITER:
5607 return text[pos] == 'X';
5608 case WB_LEFT:
5609 case WB_MOVEWORDLEFT:
5610 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5611 return pos-1;
5612 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5613 case WB_LEFTBREAK:
5614 pos--;
5615 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5616 pos--;
5617 return pos;
5618 case WB_RIGHT:
5619 case WB_MOVEWORDRIGHT:
5620 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5621 return pos+1;
5622 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5623 case WB_RIGHTBREAK:
5624 pos++;
5625 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5626 pos++;
5627 return pos;
5628 default:
5629 ok(FALSE, "Unexpected code %d\n", code);
5630 break;
5631 }
5632 return 0;
5633 }
5634
5635 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5636 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5637
5638 static void test_word_movement(void)
5639 {
5640 HWND hwnd;
5641 int result;
5642 int sel_start, sel_end;
5643 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5644
5645 /* multi-line control inserts CR normally */
5646 hwnd = new_richedit(NULL);
5647
5648 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5649 ok (result == TRUE, "Failed to clear the text.\n");
5650 SendMessage(hwnd, EM_SETSEL, 0, 0);
5651 /* |one two three */
5652
5653 SEND_CTRL_RIGHT(hwnd);
5654 /* one |two three */
5655 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5656 ok(sel_start == sel_end, "Selection should be empty\n");
5657 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5658
5659 SEND_CTRL_RIGHT(hwnd);
5660 /* one two |three */
5661 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5662 ok(sel_start == sel_end, "Selection should be empty\n");
5663 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5664
5665 SEND_CTRL_LEFT(hwnd);
5666 /* one |two three */
5667 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5668 ok(sel_start == sel_end, "Selection should be empty\n");
5669 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5670
5671 SEND_CTRL_LEFT(hwnd);
5672 /* |one two three */
5673 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5674 ok(sel_start == sel_end, "Selection should be empty\n");
5675 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5676
5677 SendMessage(hwnd, EM_SETSEL, 8, 8);
5678 /* one two | three */
5679 SEND_CTRL_RIGHT(hwnd);
5680 /* one two |three */
5681 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5682 ok(sel_start == sel_end, "Selection should be empty\n");
5683 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5684
5685 SendMessage(hwnd, EM_SETSEL, 11, 11);
5686 /* one two th|ree */
5687 SEND_CTRL_LEFT(hwnd);
5688 /* one two |three */
5689 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5690 ok(sel_start == sel_end, "Selection should be empty\n");
5691 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5692
5693 /* Test with a custom word break procedure that uses X as the delimiter. */
5694 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5695 ok (result == TRUE, "Failed to clear the text.\n");
5696 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5697 /* |one twoXthree */
5698 SEND_CTRL_RIGHT(hwnd);
5699 /* one twoX|three */
5700 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5701 ok(sel_start == sel_end, "Selection should be empty\n");
5702 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5703
5704 DestroyWindow(hwnd);
5705
5706 /* Make sure the behaviour is the same with a unicode richedit window,
5707 * and using unicode functions. */
5708 if (is_win9x)
5709 {
5710 skip("Cannot test with unicode richedit window\n");
5711 return;
5712 }
5713
5714 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5715 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5716 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5717
5718 /* Test with a custom word break procedure that uses X as the delimiter. */
5719 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5720 ok (result == TRUE, "Failed to clear the text.\n");
5721 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5722 /* |one twoXthree */
5723 SEND_CTRL_RIGHT(hwnd);
5724 /* one twoX|three */
5725 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5726 ok(sel_start == sel_end, "Selection should be empty\n");
5727 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5728
5729 DestroyWindow(hwnd);
5730 }
5731
5732 static void test_EM_CHARFROMPOS(void)
5733 {
5734 HWND hwnd;
5735 int result;
5736 RECT rcClient;
5737 POINTL point;
5738 point.x = 0;
5739 point.y = 40;
5740
5741 /* multi-line control inserts CR normally */
5742 hwnd = new_richedit(NULL);
5743 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5744 (LPARAM)"one two three four five six seven\reight");
5745
5746 GetClientRect(hwnd, &rcClient);
5747
5748 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5749 ok(result == 34, "expected character index of 34 but got %d\n", result);
5750
5751 /* Test with points outside the bounds of the richedit control. */
5752 point.x = -1;
5753 point.y = 40;
5754 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5755 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5756
5757 point.x = 1000;
5758 point.y = 0;
5759 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5760 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5761
5762 point.x = 1000;
5763 point.y = 40;
5764 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5765 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5766
5767 point.x = 1000;
5768 point.y = -1;
5769 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5770 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5771
5772 point.x = 1000;
5773 point.y = rcClient.bottom + 1;
5774 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5775 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5776
5777 point.x = 1000;
5778 point.y = rcClient.bottom;
5779 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5780 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5781
5782 DestroyWindow(hwnd);
5783 }
5784
5785 static void test_word_wrap(void)
5786 {
5787 HWND hwnd;
5788 POINTL point = {0, 60}; /* This point must be below the first line */
5789 const char *text = "Must be long enough to test line wrapping";
5790 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5791 int res, pos, lines;
5792
5793 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5794 * when specified on window creation and set later. */
5795 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5796 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5797 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5798 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5799 ok(res, "WM_SETTEXT failed.\n");
5800 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5801 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5802 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5803 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5804
5805 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5806 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5807 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5808 DestroyWindow(hwnd);
5809
5810 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5811 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5812 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5813
5814 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5815 ok(res, "WM_SETTEXT failed.\n");
5816 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5817 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5818 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5819 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5820
5821 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5822 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5823 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5824 DestroyWindow(hwnd);
5825
5826 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5827 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5828 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5829 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5830 ok(res, "WM_SETTEXT failed.\n");
5831 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5832 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5833
5834 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5835 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5836 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5837 DestroyWindow(hwnd);
5838
5839 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5840 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5841 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5842 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5843 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5844 ok(res, "WM_SETTEXT failed.\n");
5845 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5846 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5847
5848 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5849 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5850 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5851
5852 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5853 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5854 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5855 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5856 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5857
5858 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5859 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5860 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5861 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5862 DestroyWindow(hwnd);
5863
5864 /* Test to see if wrapping happens with redraw disabled. */
5865 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5866 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5867 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5868 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5869 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5870 ok(res, "EM_REPLACESEL failed.\n");
5871 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5872 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5873 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5874 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5875 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5876
5877 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5878 DestroyWindow(hwnd);
5879 }
5880
5881 static void test_autoscroll(void)
5882 {
5883 HWND hwnd = new_richedit(NULL);
5884 int lines, ret, redraw;
5885 POINT pt;
5886
5887 for (redraw = 0; redraw <= 1; redraw++) {
5888 trace("testing with WM_SETREDRAW=%d\n", redraw);
5889 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5890 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5891 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5892 ok(lines == 8, "%d lines instead of 8\n", lines);
5893 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5894 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5895 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5896 ret = GetWindowLong(hwnd, GWL_STYLE);
5897 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5898
5899 SendMessage(hwnd, WM_SETTEXT, 0, 0);
5900 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5901 ok(lines == 1, "%d lines instead of 1\n", lines);
5902 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5903 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5904 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5905 ret = GetWindowLong(hwnd, GWL_STYLE);
5906 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5907 }
5908
5909 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5910 DestroyWindow(hwnd);
5911
5912 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
5913 * auto vertical/horizontal scrolling options. */
5914 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5915 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
5916 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5917 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5918 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
5919 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
5920 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
5921 ret = GetWindowLong(hwnd, GWL_STYLE);
5922 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
5923 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
5924 DestroyWindow(hwnd);
5925
5926 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5927 WS_POPUP|ES_MULTILINE,
5928 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5929 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5930 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
5931 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
5932 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
5933 ret = GetWindowLong(hwnd, GWL_STYLE);
5934 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
5935 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
5936 DestroyWindow(hwnd);
5937 }
5938
5939
5940 static void test_format_rect(void)
5941 {
5942 HWND hwnd;
5943 RECT rc, expected, clientRect;
5944 int n;
5945 DWORD options;
5946
5947 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5948 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5949 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5950 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5951
5952 GetClientRect(hwnd, &clientRect);
5953
5954 expected = clientRect;
5955 expected.left += 1;
5956 expected.right -= 1;
5957 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5958 ok(rc.top == expected.top && rc.left == expected.left &&
5959 rc.bottom == expected.bottom && rc.right == expected.right,
5960 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5961 rc.top, rc.left, rc.bottom, rc.right,
5962 expected.top, expected.left, expected.bottom, expected.right);
5963
5964 for (n = -3; n <= 3; n++)
5965 {
5966 rc = clientRect;
5967 rc.top += n;
5968 rc.left += n;
5969 rc.bottom -= n;
5970 rc.right -= n;
5971 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5972
5973 expected = rc;
5974 expected.top = max(0, rc.top);
5975 expected.left = max(0, rc.left);
5976 expected.bottom = min(clientRect.bottom, rc.bottom);
5977 expected.right = min(clientRect.right, rc.right);
5978 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5979 ok(rc.top == expected.top && rc.left == expected.left &&
5980 rc.bottom == expected.bottom && rc.right == expected.right,
5981 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5982 n, rc.top, rc.left, rc.bottom, rc.right,
5983 expected.top, expected.left, expected.bottom, expected.right);
5984 }
5985
5986 rc = clientRect;
5987 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5988 expected = clientRect;
5989 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5990 ok(rc.top == expected.top && rc.left == expected.left &&
5991 rc.bottom == expected.bottom && rc.right == expected.right,
5992 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5993 rc.top, rc.left, rc.bottom, rc.right,
5994 expected.top, expected.left, expected.bottom, expected.right);
5995
5996 /* Adding the selectionbar adds the selectionbar width to the left side. */
5997 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
5998 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
5999 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6000 expected.left += 8; /* selection bar width */
6001 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6002 ok(rc.top == expected.top && rc.left == expected.left &&
6003 rc.bottom == expected.bottom && rc.right == expected.right,
6004 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6005 rc.top, rc.left, rc.bottom, rc.right,
6006 expected.top, expected.left, expected.bottom, expected.right);
6007
6008 rc = clientRect;
6009 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6010 expected = clientRect;
6011 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6012 ok(rc.top == expected.top && rc.left == expected.left &&
6013 rc.bottom == expected.bottom && rc.right == expected.right,
6014 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6015 rc.top, rc.left, rc.bottom, rc.right,
6016 expected.top, expected.left, expected.bottom, expected.right);
6017
6018 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6019 * even if the left side is already 0. */
6020 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6021 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6022 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6023 expected.left -= 8; /* selection bar width */
6024 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6025 ok(rc.top == expected.top && rc.left == expected.left &&
6026 rc.bottom == expected.bottom && rc.right == expected.right,
6027 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6028 rc.top, rc.left, rc.bottom, rc.right,
6029 expected.top, expected.left, expected.bottom, expected.right);
6030
6031 /* Set the absolute value of the formatting rectangle. */
6032 rc = clientRect;
6033 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6034 expected = clientRect;
6035 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6036 ok(rc.top == expected.top && rc.left == expected.left &&
6037 rc.bottom == expected.bottom && rc.right == expected.right,
6038 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6039 n, rc.top, rc.left, rc.bottom, rc.right,
6040 expected.top, expected.left, expected.bottom, expected.right);
6041
6042 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6043 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6044 * tests show that this isn't true. */
6045 rc.top = 15;
6046 rc.left = 15;
6047 rc.bottom = clientRect.bottom - 15;
6048 rc.right = clientRect.right - 15;
6049 expected = rc;
6050 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6051 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6052 ok(rc.top == expected.top && rc.left == expected.left &&
6053 rc.bottom == expected.bottom && rc.right == expected.right,
6054 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6055 rc.top, rc.left, rc.bottom, rc.right,
6056 expected.top, expected.left, expected.bottom, expected.right);
6057
6058 /* For some reason it does not limit the values to the client rect with
6059 * a WPARAM value of 1. */
6060 rc.top = -15;
6061 rc.left = -15;
6062 rc.bottom = clientRect.bottom + 15;
6063 rc.right = clientRect.right + 15;
6064 expected = rc;
6065 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6066 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6067 ok(rc.top == expected.top && rc.left == expected.left &&
6068 rc.bottom == expected.bottom && rc.right == expected.right,
6069 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6070 rc.top, rc.left, rc.bottom, rc.right,
6071 expected.top, expected.left, expected.bottom, expected.right);
6072
6073 DestroyWindow(hwnd);
6074
6075 /* The extended window style affects the formatting rectangle. */
6076 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6077 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6078 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6079 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6080
6081 GetClientRect(hwnd, &clientRect);
6082
6083 expected = clientRect;
6084 expected.left += 1;
6085 expected.top += 1;
6086 expected.right -= 1;
6087 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6088 ok(rc.top == expected.top && rc.left == expected.left &&
6089 rc.bottom == expected.bottom && rc.right == expected.right,
6090 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6091 rc.top, rc.left, rc.bottom, rc.right,
6092 expected.top, expected.left, expected.bottom, expected.right);
6093
6094 rc = clientRect;
6095 rc.top += 5;
6096 rc.left += 5;
6097 rc.bottom -= 5;
6098 rc.right -= 5;
6099 expected = rc;
6100 expected.top -= 1;
6101 expected.left -= 1;
6102 expected.right += 1;
6103 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6104 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6105 ok(rc.top == expected.top && rc.left == expected.left &&
6106 rc.bottom == expected.bottom && rc.right == expected.right,
6107 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6108 rc.top, rc.left, rc.bottom, rc.right,
6109 expected.top, expected.left, expected.bottom, expected.right);
6110
6111 DestroyWindow(hwnd);
6112 }
6113
6114 static void test_WM_GETDLGCODE(void)
6115 {
6116 HWND hwnd;
6117 UINT res, expected;
6118 MSG msg;
6119
6120 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6121
6122 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6123 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6124 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6125 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6126 msg.hwnd = hwnd;
6127 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6128 expected = expected | DLGC_WANTMESSAGE;
6129 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6130 res, expected);
6131 DestroyWindow(hwnd);
6132
6133 msg.message = WM_KEYDOWN;
6134 msg.wParam = VK_RETURN;
6135 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6136 msg.pt.x = 0;
6137 msg.pt.y = 0;
6138 msg.time = GetTickCount();
6139
6140 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6141 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6142 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6143 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6144 msg.hwnd = hwnd;
6145 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6146 expected = expected | DLGC_WANTMESSAGE;
6147 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6148 res, expected);
6149 DestroyWindow(hwnd);
6150
6151 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6152 ES_MULTILINE|WS_POPUP,
6153 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6154 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6155 msg.hwnd = hwnd;
6156 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6157 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6158 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6159 res, expected);
6160 DestroyWindow(hwnd);
6161
6162 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6163 ES_WANTRETURN|WS_POPUP,
6164 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6165 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6166 msg.hwnd = hwnd;
6167 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6168 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6169 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6170 res, expected);
6171 DestroyWindow(hwnd);
6172
6173 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6174 WS_POPUP,
6175 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6176 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6177 msg.hwnd = hwnd;
6178 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6179 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6180 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6181 res, expected);
6182 DestroyWindow(hwnd);
6183
6184 msg.wParam = VK_TAB;
6185 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6186
6187 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6188 ES_MULTILINE|WS_POPUP,
6189 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6190 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6191 msg.hwnd = hwnd;
6192 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6193 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6194 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6195 res, expected);
6196 DestroyWindow(hwnd);
6197
6198 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6199 WS_POPUP,
6200 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6201 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6202 msg.hwnd = hwnd;
6203 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6204 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6205 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6206 res, expected);
6207 DestroyWindow(hwnd);
6208
6209 hold_key(VK_CONTROL);
6210
6211 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6212 ES_MULTILINE|WS_POPUP,
6213 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6214 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6215 msg.hwnd = hwnd;
6216 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6217 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6218 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6219 res, expected);
6220 DestroyWindow(hwnd);
6221
6222 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6223 WS_POPUP,
6224 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6225 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6226 msg.hwnd = hwnd;
6227 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6228 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6229 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6230 res, expected);
6231 DestroyWindow(hwnd);
6232
6233 release_key(VK_CONTROL);
6234
6235 msg.wParam = 'a';
6236 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6237
6238 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6239 ES_MULTILINE|WS_POPUP,
6240 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6241 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6242 msg.hwnd = hwnd;
6243 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6244 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6245 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6246 res, expected);
6247 DestroyWindow(hwnd);
6248
6249 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6250 WS_POPUP,
6251 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6252 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6253 msg.hwnd = hwnd;
6254 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6255 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6256 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6257 res, expected);
6258 DestroyWindow(hwnd);
6259
6260 msg.message = WM_CHAR;
6261
6262 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6263 ES_MULTILINE|WS_POPUP,
6264 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6265 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6266 msg.hwnd = hwnd;
6267 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6268 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6269 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6270 res, expected);
6271 DestroyWindow(hwnd);
6272
6273 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6274 WS_POPUP,
6275 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6276 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6277 msg.hwnd = hwnd;
6278 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6279 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6280 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6281 res, expected);
6282 DestroyWindow(hwnd);
6283 }
6284
6285 static void test_zoom(void)
6286 {
6287 HWND hwnd;
6288 UINT ret;
6289 RECT rc;
6290 POINT pt;
6291 int numerator, denominator;
6292
6293 hwnd = new_richedit(NULL);
6294 GetClientRect(hwnd, &rc);
6295 pt.x = (rc.right - rc.left) / 2;
6296 pt.y = (rc.bottom - rc.top) / 2;
6297 ClientToScreen(hwnd, &pt);
6298
6299 /* Test initial zoom value */
6300 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6301 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6302 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6303 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6304
6305 /* test scroll wheel */
6306 hold_key(VK_CONTROL);
6307 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6308 MAKELPARAM(pt.x, pt.y));
6309 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6310 release_key(VK_CONTROL);
6311
6312 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6313 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6314 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6315 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6316
6317 /* Test how much the mouse wheel can zoom in and out. */
6318 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6319 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6320
6321 hold_key(VK_CONTROL);
6322 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6323 MAKELPARAM(pt.x, pt.y));
6324 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6325 release_key(VK_CONTROL);
6326
6327 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6328 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6329 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6330 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6331
6332 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6333 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6334
6335 hold_key(VK_CONTROL);
6336 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6337 MAKELPARAM(pt.x, pt.y));
6338 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6339 release_key(VK_CONTROL);
6340
6341 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6342 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6343 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6344 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6345
6346 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6347 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6348
6349 hold_key(VK_CONTROL);
6350 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6351 MAKELPARAM(pt.x, pt.y));
6352 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6353 release_key(VK_CONTROL);
6354
6355 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6356 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6357 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6358 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6359
6360 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6361 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6362
6363 hold_key(VK_CONTROL);
6364 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6365 MAKELPARAM(pt.x, pt.y));
6366 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6367 release_key(VK_CONTROL);
6368
6369 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6370 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6371 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6372 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6373
6374 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6375 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6376 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6377
6378 hold_key(VK_CONTROL);
6379 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6380 MAKELPARAM(pt.x, pt.y));
6381 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6382 release_key(VK_CONTROL);
6383
6384 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6385 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6386 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6387 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6388
6389 /* Test bounds checking on EM_SETZOOM */
6390 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6391 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6392
6393 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6394 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6395
6396 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6397 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6398
6399 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6400 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6401 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6402 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6403
6404 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6405 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6406
6407 /* See if negative numbers are accepted. */
6408 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6409 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6410
6411 /* See if negative numbers are accepted. */
6412 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6413 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6414
6415 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6416 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6417 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6418 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6419
6420 /* Reset the zoom value */
6421 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6422 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6423
6424 DestroyWindow(hwnd);
6425 }
6426
6427 START_TEST( editor )
6428 {
6429 /* Must explicitly LoadLibrary(). The test has no references to functions in
6430 * RICHED20.DLL, so the linker doesn't actually link to it. */
6431 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6432 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6433
6434 is_win9x = GetVersion() & 0x80000000;
6435
6436 test_WM_CHAR();
6437 test_EM_FINDTEXT();
6438 test_EM_GETLINE();
6439 test_EM_POSFROMCHAR();
6440 test_EM_SCROLLCARET();
6441 test_EM_SCROLL();
6442 test_scrollbar_visibility();
6443 test_WM_SETTEXT();
6444 test_EM_LINELENGTH();
6445 test_EM_SETCHARFORMAT();
6446 test_EM_SETTEXTMODE();
6447 test_TM_PLAINTEXT();
6448 test_EM_SETOPTIONS();
6449 test_WM_GETTEXT();
6450 test_EM_GETTEXTRANGE();
6451 test_EM_GETSELTEXT();
6452 test_EM_SETUNDOLIMIT();
6453 test_ES_PASSWORD();
6454 test_EM_SETTEXTEX();
6455 test_EM_LIMITTEXT();
6456 test_EM_EXLIMITTEXT();
6457 test_EM_GETLIMITTEXT();
6458 test_WM_SETFONT();
6459 test_EM_GETMODIFY();
6460 test_EM_EXSETSEL();
6461 test_WM_PASTE();
6462 test_EM_STREAMIN();
6463 test_EM_STREAMOUT();
6464 test_EM_StreamIn_Undo();
6465 test_EM_FORMATRANGE();
6466 test_unicode_conversions();
6467 test_EM_GETTEXTLENGTHEX();
6468 test_EM_REPLACESEL(1);
6469 test_EM_REPLACESEL(0);
6470 test_WM_NOTIFY();
6471 test_EM_AUTOURLDETECT();
6472 test_eventMask();
6473 test_undo_coalescing();
6474 test_word_movement();
6475 test_EM_CHARFROMPOS();
6476 test_SETPARAFORMAT();
6477 test_word_wrap();
6478 test_autoscroll();
6479 test_format_rect();
6480 test_WM_GETDLGCODE();
6481 test_zoom();
6482
6483 /* Set the environment variable WINETEST_RICHED20 to keep windows
6484 * responsive and open for 30 seconds. This is useful for debugging.
6485 */
6486 if (getenv( "WINETEST_RICHED20" )) {
6487 keep_responsive(30);
6488 }
6489
6490 OleFlushClipboard();
6491 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
6492 }