sync rostests to r44455
[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 /* cpMax of text length is used instead of -1 in this case */
1424 textRange.lpstrText = buffer;
1425 textRange.chrg.cpMin = 0;
1426 textRange.chrg.cpMax = -1;
1427 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1428 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1429 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1430
1431 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1432 textRange.lpstrText = buffer;
1433 textRange.chrg.cpMin = -1;
1434 textRange.chrg.cpMax = 1;
1435 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1436 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1437 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1438
1439 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1440 textRange.lpstrText = buffer;
1441 textRange.chrg.cpMin = 1;
1442 textRange.chrg.cpMax = -1;
1443 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1444 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1445 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446
1447 /* no end character is copied if cpMax - cpMin < 0 */
1448 textRange.lpstrText = buffer;
1449 textRange.chrg.cpMin = 5;
1450 textRange.chrg.cpMax = 5;
1451 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1452 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1453 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454
1455 /* cpMax of text length is used if cpMax > text length*/
1456 textRange.lpstrText = buffer;
1457 textRange.chrg.cpMin = 0;
1458 textRange.chrg.cpMax = 1000;
1459 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1460 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1461 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1462
1463 DestroyWindow(hwndRichEdit);
1464 }
1465
1466 static void test_EM_GETSELTEXT(void)
1467 {
1468 HWND hwndRichEdit = new_richedit(NULL);
1469 const char * text1 = "foo bar\r\nfoo bar";
1470 const char * text2 = "foo bar\rfoo bar";
1471 const char * expect = "bar\rfoo";
1472 char buffer[1024] = {0};
1473 LRESULT result;
1474
1475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1476
1477 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1478 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1479 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1481
1482 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1483
1484 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1485 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1486 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488
1489 DestroyWindow(hwndRichEdit);
1490 }
1491
1492 /* FIXME: need to test unimplemented options and robustly test wparam */
1493 static void test_EM_SETOPTIONS(void)
1494 {
1495 HWND hwndRichEdit;
1496 static const char text[] = "Hello. My name is RichEdit!";
1497 char buffer[1024] = {0};
1498 DWORD dwStyle, options, oldOptions;
1499 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1500 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1501 ES_SELECTIONBAR|ES_VERTICAL;
1502
1503 /* Test initial options. */
1504 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1505 0, 0, 200, 60, NULL, NULL,
1506 hmoduleRichEdit, NULL);
1507 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1508 RICHEDIT_CLASS, (int) GetLastError());
1509 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1510 ok(options == 0, "Incorrect initial options %x\n", options);
1511 DestroyWindow(hwndRichEdit);
1512
1513 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1514 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1515 0, 0, 200, 60, NULL, NULL,
1516 hmoduleRichEdit, NULL);
1517 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1518 RICHEDIT_CLASS, (int) GetLastError());
1519 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1520 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1521 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1522 "Incorrect initial options %x\n", options);
1523
1524 /* NEGATIVE TESTING - NO OPTIONS SET */
1525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1526 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1527
1528 /* testing no readonly by sending 'a' to the control*/
1529 SetFocus(hwndRichEdit);
1530 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1531 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1532 ok(buffer[0]=='a',
1533 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1535
1536 /* READONLY - sending 'a' to the control */
1537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1538 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1539 SetFocus(hwndRichEdit);
1540 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1541 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1542 ok(buffer[0]==text[0],
1543 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1544
1545 /* EM_SETOPTIONS changes the window style, but changing the
1546 * window style does not change the options. */
1547 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1548 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1549 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1550 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1551 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1552 /* Confirm that the text is still read only. */
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]==text[0],
1556 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1557
1558 oldOptions = options;
1559 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1560 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1561 ok(options == oldOptions,
1562 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1563
1564 DestroyWindow(hwndRichEdit);
1565 }
1566
1567 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1568 {
1569 CHARFORMAT2W text_format;
1570 text_format.cbSize = sizeof(text_format);
1571 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1572 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1573 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1574 }
1575
1576 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1577 {
1578 int link_present = 0;
1579
1580 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1581 if (is_url)
1582 { /* control text is url; should get CFE_LINK */
1583 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1584 }
1585 else
1586 {
1587 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1588 }
1589 }
1590
1591 static HWND new_static_wnd(HWND parent) {
1592 return new_window("Static", 0, parent);
1593 }
1594
1595 static void test_EM_AUTOURLDETECT(void)
1596 {
1597 /* DO NOT change the properties of the first two elements. To shorten the
1598 tests, all tests after WM_SETTEXT test just the first two elements -
1599 one non-URL and one URL */
1600 struct urls_s {
1601 const char *text;
1602 int is_url;
1603 } urls[12] = {
1604 {"winehq.org", 0},
1605 {"http://www.winehq.org", 1},
1606 {"http//winehq.org", 0},
1607 {"ww.winehq.org", 0},
1608 {"www.winehq.org", 1},
1609 {"ftp://192.168.1.1", 1},
1610 {"ftp//192.168.1.1", 0},
1611 {"mailto:your@email.com", 1},
1612 {"prospero:prosperoserver", 1},
1613 {"telnet:test", 1},
1614 {"news:newserver", 1},
1615 {"wais:waisserver", 1}
1616 };
1617
1618 int i, j;
1619 int urlRet=-1;
1620 HWND hwndRichEdit, parent;
1621
1622 /* All of the following should cause the URL to be detected */
1623 const char * templates_delim[] = {
1624 "This is some text with X on it",
1625 "This is some text with (X) on it",
1626 "This is some text with X\r on it",
1627 "This is some text with ---X--- on it",
1628 "This is some text with \"X\" on it",
1629 "This is some text with 'X' on it",
1630 "This is some text with 'X' on it",
1631 "This is some text with :X: on it",
1632
1633 "This text ends with X",
1634
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 "This is some text with X: on it",
1640
1641 "This is some text with (X on it",
1642 "This is some text with \rX on it",
1643 "This is some text with ---X on it",
1644 "This is some text with \"X on it",
1645 "This is some text with 'X on it",
1646 "This is some text with :X on it",
1647 };
1648 /* None of these should cause the URL to be detected */
1649 const char * templates_non_delim[] = {
1650 "This is some text with |X| on it",
1651 "This is some text with *X* on it",
1652 "This is some text with /X/ on it",
1653 "This is some text with +X+ on it",
1654 "This is some text with %X% on it",
1655 "This is some text with #X# on it",
1656 "This is some text with @X@ on it",
1657 "This is some text with \\X\\ on it",
1658 "This is some text with |X on it",
1659 "This is some text with *X on it",
1660 "This is some text with /X on it",
1661 "This is some text with +X on it",
1662 "This is some text with %X on it",
1663 "This is some text with #X on it",
1664 "This is some text with @X on it",
1665 "This is some text with \\X on it",
1666 };
1667 /* All of these cause the URL detection to be extended by one more byte,
1668 thus demonstrating that the tested character is considered as part
1669 of the URL. */
1670 const char * templates_xten_delim[] = {
1671 "This is some text with X| on it",
1672 "This is some text with X* on it",
1673 "This is some text with X/ on it",
1674 "This is some text with X+ on it",
1675 "This is some text with X% on it",
1676 "This is some text with X# on it",
1677 "This is some text with X@ on it",
1678 "This is some text with X\\ on it",
1679 };
1680 char buffer[1024];
1681
1682 parent = new_static_wnd(NULL);
1683 hwndRichEdit = new_richedit(parent);
1684 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1685 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1686 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1687 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1688 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1689 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1690 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1691 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1692 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1693 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1694 /* for each url, check the text to see if CFE_LINK effect is present */
1695 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1696
1697 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1699 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1700
1701 /* Link detection should happen immediately upon WM_SETTEXT */
1702 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1704 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1705 }
1706 DestroyWindow(hwndRichEdit);
1707
1708 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1709 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710 hwndRichEdit = new_richedit(parent);
1711
1712 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1713 char * at_pos;
1714 int at_offset;
1715 int end_offset;
1716
1717 at_pos = strchr(templates_delim[j], 'X');
1718 at_offset = at_pos - templates_delim[j];
1719 strncpy(buffer, templates_delim[j], at_offset);
1720 buffer[at_offset] = '\0';
1721 strcat(buffer, urls[i].text);
1722 strcat(buffer, templates_delim[j] + at_offset + 1);
1723 end_offset = at_offset + strlen(urls[i].text);
1724
1725 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1727
1728 /* This assumes no templates start with the URL itself, and that they
1729 have at least two characters before the URL text */
1730 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1731 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1732 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1733 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1736
1737 if (urls[i].is_url)
1738 {
1739 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1740 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1741 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1742 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1743 }
1744 else
1745 {
1746 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1747 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1750 }
1751 if (buffer[end_offset] != '\0')
1752 {
1753 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1754 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1755 if (buffer[end_offset +1] != '\0')
1756 {
1757 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1759 }
1760 }
1761 }
1762
1763 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1764 char * at_pos;
1765 int at_offset;
1766 int end_offset;
1767
1768 at_pos = strchr(templates_non_delim[j], 'X');
1769 at_offset = at_pos - templates_non_delim[j];
1770 strncpy(buffer, templates_non_delim[j], at_offset);
1771 buffer[at_offset] = '\0';
1772 strcat(buffer, urls[i].text);
1773 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1774 end_offset = at_offset + strlen(urls[i].text);
1775
1776 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1778
1779 /* This assumes no templates start with the URL itself, and that they
1780 have at least two characters before the URL text */
1781 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1782 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1783 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1784 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1785 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1786 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1787
1788 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1789 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1790 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1791 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1792 if (buffer[end_offset] != '\0')
1793 {
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1796 if (buffer[end_offset +1] != '\0')
1797 {
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1800 }
1801 }
1802 }
1803
1804 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1805 char * at_pos;
1806 int at_offset;
1807 int end_offset;
1808
1809 at_pos = strchr(templates_xten_delim[j], 'X');
1810 at_offset = at_pos - templates_xten_delim[j];
1811 strncpy(buffer, templates_xten_delim[j], at_offset);
1812 buffer[at_offset] = '\0';
1813 strcat(buffer, urls[i].text);
1814 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1815 end_offset = at_offset + strlen(urls[i].text);
1816
1817 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1818 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1819
1820 /* This assumes no templates start with the URL itself, and that they
1821 have at least two characters before the URL text */
1822 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1823 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1828
1829 if (urls[i].is_url)
1830 {
1831 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1832 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1833 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1834 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1835 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1836 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1837 }
1838 else
1839 {
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1846 }
1847 if (buffer[end_offset +1] != '\0')
1848 {
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1851 if (buffer[end_offset +2] != '\0')
1852 {
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1855 }
1856 }
1857 }
1858
1859 DestroyWindow(hwndRichEdit);
1860 hwndRichEdit = NULL;
1861 }
1862
1863 /* Test detection of URLs within normal text - WM_CHAR case. */
1864 /* Test only the first two URL examples for brevity */
1865 for (i = 0; i < 2; i++) {
1866 hwndRichEdit = new_richedit(parent);
1867
1868 /* Also for brevity, test only the first three delimiters */
1869 for (j = 0; j < 3; j++) {
1870 char * at_pos;
1871 int at_offset;
1872 int end_offset;
1873 int u, v;
1874
1875 at_pos = strchr(templates_delim[j], 'X');
1876 at_offset = at_pos - templates_delim[j];
1877 end_offset = at_offset + strlen(urls[i].text);
1878
1879 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1880 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1881 for (u = 0; templates_delim[j][u]; u++) {
1882 if (templates_delim[j][u] == '\r') {
1883 simulate_typing_characters(hwndRichEdit, "\r");
1884 } else if (templates_delim[j][u] != 'X') {
1885 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1886 } else {
1887 for (v = 0; urls[i].text[v]; v++) {
1888 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1889 }
1890 }
1891 }
1892 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1893
1894 /* This assumes no templates start with the URL itself, and that they
1895 have at least two characters before the URL text */
1896 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1897 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1902
1903 if (urls[i].is_url)
1904 {
1905 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1906 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1907 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1908 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1909 }
1910 else
1911 {
1912 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1916 }
1917 if (buffer[end_offset] != '\0')
1918 {
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1921 if (buffer[end_offset +1] != '\0')
1922 {
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1925 }
1926 }
1927
1928 /* The following will insert a paragraph break after the first character
1929 of the URL candidate, thus breaking the URL. It is expected that the
1930 CFE_LINK attribute should break across both pieces of the URL */
1931 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1932 simulate_typing_characters(hwndRichEdit, "\r");
1933 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1934
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1941
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1944 /* end_offset moved because of paragraph break */
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1947 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1948 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1949 {
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1952 if (buffer[end_offset +2] != '\0')
1953 {
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1956 }
1957 }
1958
1959 /* The following will remove the just-inserted paragraph break, thus
1960 restoring the URL */
1961 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1962 simulate_typing_characters(hwndRichEdit, "\b");
1963 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1964
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1971
1972 if (urls[i].is_url)
1973 {
1974 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1975 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1976 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1977 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1978 }
1979 else
1980 {
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1985 }
1986 if (buffer[end_offset] != '\0')
1987 {
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1990 if (buffer[end_offset +1] != '\0')
1991 {
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1994 }
1995 }
1996 }
1997 DestroyWindow(hwndRichEdit);
1998 hwndRichEdit = NULL;
1999 }
2000
2001 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2002 /* Test just the first two URL examples for brevity */
2003 for (i = 0; i < 2; i++) {
2004 SETTEXTEX st;
2005
2006 hwndRichEdit = new_richedit(parent);
2007
2008 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2009 be detected:
2010 1) Set entire text, a la WM_SETTEXT
2011 2) Set a selection of the text to the URL
2012 3) Set a portion of the text at a time, which eventually results in
2013 an URL
2014 All of them should give equivalent results
2015 */
2016
2017 /* Set entire text in one go, like WM_SETTEXT */
2018 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2019 char * at_pos;
2020 int at_offset;
2021 int end_offset;
2022
2023 st.codepage = CP_ACP;
2024 st.flags = ST_DEFAULT;
2025
2026 at_pos = strchr(templates_delim[j], 'X');
2027 at_offset = at_pos - templates_delim[j];
2028 strncpy(buffer, templates_delim[j], at_offset);
2029 buffer[at_offset] = '\0';
2030 strcat(buffer, urls[i].text);
2031 strcat(buffer, templates_delim[j] + at_offset + 1);
2032 end_offset = at_offset + strlen(urls[i].text);
2033
2034 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2035 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2036
2037 /* This assumes no templates start with the URL itself, and that they
2038 have at least two characters before the URL text */
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2043 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2044 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2045
2046 if (urls[i].is_url)
2047 {
2048 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2049 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2050 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2052 }
2053 else
2054 {
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2059 }
2060 if (buffer[end_offset] != '\0')
2061 {
2062 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2063 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2064 if (buffer[end_offset +1] != '\0')
2065 {
2066 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2067 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2068 }
2069 }
2070 }
2071
2072 /* Set selection with X to the URL */
2073 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2074 char * at_pos;
2075 int at_offset;
2076 int end_offset;
2077
2078 at_pos = strchr(templates_delim[j], 'X');
2079 at_offset = at_pos - templates_delim[j];
2080 end_offset = at_offset + strlen(urls[i].text);
2081
2082 st.codepage = CP_ACP;
2083 st.flags = ST_DEFAULT;
2084 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2085 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2086 st.flags = ST_SELECTION;
2087 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2088 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2089 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2090
2091 /* This assumes no templates start with the URL itself, and that they
2092 have at least two characters before the URL text */
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2099
2100 if (urls[i].is_url)
2101 {
2102 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2103 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2104 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2105 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2106 }
2107 else
2108 {
2109 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2110 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2111 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2112 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2113 }
2114 if (buffer[end_offset] != '\0')
2115 {
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2118 if (buffer[end_offset +1] != '\0')
2119 {
2120 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2121 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2122 }
2123 }
2124 }
2125
2126 /* Set selection with X to the first character of the URL, then the rest */
2127 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2128 char * at_pos;
2129 int at_offset;
2130 int end_offset;
2131
2132 at_pos = strchr(templates_delim[j], 'X');
2133 at_offset = at_pos - templates_delim[j];
2134 end_offset = at_offset + strlen(urls[i].text);
2135
2136 strcpy(buffer, "YY");
2137 buffer[0] = urls[i].text[0];
2138
2139 st.codepage = CP_ACP;
2140 st.flags = ST_DEFAULT;
2141 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2143 st.flags = ST_SELECTION;
2144 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2146 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2147 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2148 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2149
2150 /* This assumes no templates start with the URL itself, and that they
2151 have at least two characters before the URL text */
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2158
2159 if (urls[i].is_url)
2160 {
2161 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2163 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165 }
2166 else
2167 {
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2172 }
2173 if (buffer[end_offset] != '\0')
2174 {
2175 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2176 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2177 if (buffer[end_offset +1] != '\0')
2178 {
2179 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2180 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2181 }
2182 }
2183 }
2184
2185 DestroyWindow(hwndRichEdit);
2186 hwndRichEdit = NULL;
2187 }
2188
2189 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2190 /* Test just the first two URL examples for brevity */
2191 for (i = 0; i < 2; i++) {
2192 hwndRichEdit = new_richedit(parent);
2193
2194 /* Set selection with X to the URL */
2195 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2196 char * at_pos;
2197 int at_offset;
2198 int end_offset;
2199
2200 at_pos = strchr(templates_delim[j], 'X');
2201 at_offset = at_pos - templates_delim[j];
2202 end_offset = at_offset + strlen(urls[i].text);
2203
2204 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2206 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2207 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2208 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2209
2210 /* This assumes no templates start with the URL itself, and that they
2211 have at least two characters before the URL text */
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2218
2219 if (urls[i].is_url)
2220 {
2221 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2222 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2223 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2224 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2225 }
2226 else
2227 {
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2232 }
2233 if (buffer[end_offset] != '\0')
2234 {
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2237 if (buffer[end_offset +1] != '\0')
2238 {
2239 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2240 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2241 }
2242 }
2243 }
2244
2245 /* Set selection with X to the first character of the URL, then the rest */
2246 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2247 char * at_pos;
2248 int at_offset;
2249 int end_offset;
2250
2251 at_pos = strchr(templates_delim[j], 'X');
2252 at_offset = at_pos - templates_delim[j];
2253 end_offset = at_offset + strlen(urls[i].text);
2254
2255 strcpy(buffer, "YY");
2256 buffer[0] = urls[i].text[0];
2257
2258 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2260 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2261 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2262 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2263 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2264 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2265
2266 /* This assumes no templates start with the URL itself, and that they
2267 have at least two characters before the URL text */
2268 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2269 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2272 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2273 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2274
2275 if (urls[i].is_url)
2276 {
2277 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2278 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2279 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2280 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2281 }
2282 else
2283 {
2284 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2285 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2286 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2287 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2288 }
2289 if (buffer[end_offset] != '\0')
2290 {
2291 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2292 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2293 if (buffer[end_offset +1] != '\0')
2294 {
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2297 }
2298 }
2299 }
2300
2301 DestroyWindow(hwndRichEdit);
2302 hwndRichEdit = NULL;
2303 }
2304
2305 DestroyWindow(parent);
2306 }
2307
2308 static void test_EM_SCROLL(void)
2309 {
2310 int i, j;
2311 int r; /* return value */
2312 int expr; /* expected return value */
2313 HWND hwndRichEdit = new_richedit(NULL);
2314 int y_before, y_after; /* units of lines of text */
2315
2316 /* test a richedit box containing a single line of text */
2317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2318 expr = 0x00010000;
2319 for (i = 0; i < 4; i++) {
2320 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2321
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2325 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2326 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2327 "(i == %d)\n", y_after, i);
2328 }
2329
2330 /*
2331 * test a richedit box that will scroll. There are two general
2332 * cases: the case without any long lines and the case with a long
2333 * line.
2334 */
2335 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2336 if (i == 0)
2337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2338 else
2339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2340 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2341 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2342 "LONG LINE \nb\nc\nd\ne");
2343 for (j = 0; j < 12; j++) /* reset scroll position to top */
2344 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2345
2346 /* get first visible line */
2347 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2348 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2349
2350 /* get new current first visible line */
2351 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2352
2353 ok(((r & 0xffffff00) == 0x00010000) &&
2354 ((r & 0x000000ff) != 0x00000000),
2355 "EM_SCROLL page down didn't scroll by a small positive number of "
2356 "lines (r == 0x%08x)\n", r);
2357 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2358 "(line %d scrolled to line %d\n", y_before, y_after);
2359
2360 y_before = y_after;
2361
2362 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2363 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2364 ok(((r & 0xffffff00) == 0x0001ff00),
2365 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2366 "(r == 0x%08x)\n", r);
2367 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2368 "%d scrolled to line %d\n", y_before, y_after);
2369
2370 y_before = y_after;
2371
2372 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2373
2374 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2375
2376 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2377 "(r == 0x%08x)\n", r);
2378 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2379 "1 line (%d scrolled to %d)\n", y_before, y_after);
2380
2381 y_before = y_after;
2382
2383 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2384
2385 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2386
2387 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2388 "(r == 0x%08x)\n", r);
2389 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2390 "line (%d scrolled to %d)\n", y_before, y_after);
2391
2392 y_before = y_after;
2393
2394 r = SendMessage(hwndRichEdit, EM_SCROLL,
2395 SB_LINEUP, 0); /* lineup beyond top */
2396
2397 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2398
2399 ok(r == 0x00010000,
2400 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2401 ok(y_before == y_after,
2402 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2403
2404 y_before = y_after;
2405
2406 r = SendMessage(hwndRichEdit, EM_SCROLL,
2407 SB_PAGEUP, 0);/*page up beyond top */
2408
2409 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2410
2411 ok(r == 0x00010000,
2412 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2413 ok(y_before == y_after,
2414 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2415
2416 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2417 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2418 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_PAGEDOWN, 0); /* page down beyond bot */
2421 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2422
2423 ok(r == 0x00010000,
2424 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2425 ok(y_before == y_after,
2426 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2427 y_before, y_after);
2428
2429 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2430 SendMessage(hwndRichEdit, EM_SCROLL,
2431 SB_LINEDOWN, 0); /* line down beyond bot */
2432 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2433
2434 ok(r == 0x00010000,
2435 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2436 ok(y_before == y_after,
2437 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2438 y_before, y_after);
2439 }
2440 DestroyWindow(hwndRichEdit);
2441 }
2442
2443 unsigned int recursionLevel = 0;
2444 unsigned int WM_SIZE_recursionLevel = 0;
2445 BOOL bailedOutOfRecursion = FALSE;
2446 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2447
2448 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2449 {
2450 LRESULT r;
2451
2452 if (bailedOutOfRecursion) return 0;
2453 if (recursionLevel >= 32) {
2454 bailedOutOfRecursion = TRUE;
2455 return 0;
2456 }
2457
2458 recursionLevel++;
2459 switch (message) {
2460 case WM_SIZE:
2461 WM_SIZE_recursionLevel++;
2462 r = richeditProc(hwnd, message, wParam, lParam);
2463 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2464 ShowScrollBar(hwnd, SB_VERT, TRUE);
2465 WM_SIZE_recursionLevel--;
2466 break;
2467 default:
2468 r = richeditProc(hwnd, message, wParam, lParam);
2469 break;
2470 }
2471 recursionLevel--;
2472 return r;
2473 }
2474
2475 static void test_scrollbar_visibility(void)
2476 {
2477 HWND hwndRichEdit;
2478 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2479 SCROLLINFO si;
2480 WNDCLASSA cls;
2481 BOOL r;
2482
2483 /* These tests show that richedit should temporarily refrain from automatically
2484 hiding or showing its scrollbars (vertical at least) when an explicit request
2485 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2486 Some applications depend on forced showing (when otherwise richedit would
2487 hide the vertical scrollbar) and are thrown on an endless recursive loop
2488 if richedit auto-hides the scrollbar again. Apparently they never heard of
2489 the ES_DISABLENOSCROLL style... */
2490
2491 hwndRichEdit = new_richedit(NULL);
2492
2493 /* Test default scrollbar visibility behavior */
2494 memset(&si, 0, sizeof(si));
2495 si.cbSize = sizeof(si);
2496 si.fMask = SIF_PAGE | SIF_RANGE;
2497 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2498 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2499 "Vertical scrollbar is visible, should be invisible.\n");
2500 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2501 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2502 si.nPage, si.nMin, si.nMax);
2503
2504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2505 memset(&si, 0, sizeof(si));
2506 si.cbSize = sizeof(si);
2507 si.fMask = SIF_PAGE | SIF_RANGE;
2508 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2510 "Vertical scrollbar is visible, should be invisible.\n");
2511 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2512 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2513 si.nPage, si.nMin, si.nMax);
2514
2515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2516 memset(&si, 0, sizeof(si));
2517 si.cbSize = sizeof(si);
2518 si.fMask = SIF_PAGE | SIF_RANGE;
2519 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2520 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2521 "Vertical scrollbar is invisible, should be visible.\n");
2522 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2523 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2524 si.nPage, si.nMin, si.nMax);
2525
2526 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2527 even though it hides the scrollbar */
2528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2529 memset(&si, 0, sizeof(si));
2530 si.cbSize = sizeof(si);
2531 si.fMask = SIF_PAGE | SIF_RANGE;
2532 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2534 "Vertical scrollbar is visible, should be invisible.\n");
2535 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2536 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537 si.nPage, si.nMin, si.nMax);
2538
2539 /* Setting non-scrolling text again does *not* reset scrollbar range */
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549 si.nPage, si.nMin, si.nMax);
2550
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2557 "Vertical scrollbar is visible, should be invisible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2561
2562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2563 memset(&si, 0, sizeof(si));
2564 si.cbSize = sizeof(si);
2565 si.fMask = SIF_PAGE | SIF_RANGE;
2566 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2567 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2568 "Vertical scrollbar is visible, should be invisible.\n");
2569 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2570 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2571 si.nPage, si.nMin, si.nMax);
2572
2573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2574 memset(&si, 0, sizeof(si));
2575 si.cbSize = sizeof(si);
2576 si.fMask = SIF_PAGE | SIF_RANGE;
2577 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2578 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2579 "Vertical scrollbar is visible, should be invisible.\n");
2580 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2581 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2582 si.nPage, si.nMin, si.nMax);
2583
2584 DestroyWindow(hwndRichEdit);
2585
2586 /* Test again, with ES_DISABLENOSCROLL style */
2587 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2588
2589 /* Test default scrollbar visibility behavior */
2590 memset(&si, 0, sizeof(si));
2591 si.cbSize = sizeof(si);
2592 si.fMask = SIF_PAGE | SIF_RANGE;
2593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595 "Vertical scrollbar is invisible, should be visible.\n");
2596 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2597 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2598 si.nPage, si.nMin, si.nMax);
2599
2600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601 memset(&si, 0, sizeof(si));
2602 si.cbSize = sizeof(si);
2603 si.fMask = SIF_PAGE | SIF_RANGE;
2604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2606 "Vertical scrollbar is invisible, should be visible.\n");
2607 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2608 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2609 si.nPage, si.nMin, si.nMax);
2610
2611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2612 memset(&si, 0, sizeof(si));
2613 si.cbSize = sizeof(si);
2614 si.fMask = SIF_PAGE | SIF_RANGE;
2615 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2617 "Vertical scrollbar is invisible, should be visible.\n");
2618 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2619 "reported page/range is %d (%d..%d)\n",
2620 si.nPage, si.nMin, si.nMax);
2621
2622 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629 "Vertical scrollbar is invisible, should be visible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632 si.nPage, si.nMin, si.nMax);
2633
2634 /* Setting non-scrolling text again does *not* reset scrollbar range */
2635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2636 memset(&si, 0, sizeof(si));
2637 si.cbSize = sizeof(si);
2638 si.fMask = SIF_PAGE | SIF_RANGE;
2639 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641 "Vertical scrollbar is invisible, should be visible.\n");
2642 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644 si.nPage, si.nMin, si.nMax);
2645
2646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2647 memset(&si, 0, sizeof(si));
2648 si.cbSize = sizeof(si);
2649 si.fMask = SIF_PAGE | SIF_RANGE;
2650 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2651 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2652 "Vertical scrollbar is invisible, should be visible.\n");
2653 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2654 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2655 si.nPage, si.nMin, si.nMax);
2656
2657 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2658 memset(&si, 0, sizeof(si));
2659 si.cbSize = sizeof(si);
2660 si.fMask = SIF_PAGE | SIF_RANGE;
2661 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2662 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2663 "Vertical scrollbar is invisible, should be visible.\n");
2664 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2665 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2666 si.nPage, si.nMin, si.nMax);
2667
2668 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2669 memset(&si, 0, sizeof(si));
2670 si.cbSize = sizeof(si);
2671 si.fMask = SIF_PAGE | SIF_RANGE;
2672 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2673 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2674 "Vertical scrollbar is invisible, should be visible.\n");
2675 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2676 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2677 si.nPage, si.nMin, si.nMax);
2678
2679 DestroyWindow(hwndRichEdit);
2680
2681 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2682 hwndRichEdit = new_richedit(NULL);
2683
2684 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2685 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2686 memset(&si, 0, sizeof(si));
2687 si.cbSize = sizeof(si);
2688 si.fMask = SIF_PAGE | SIF_RANGE;
2689 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2690 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2691 "Vertical scrollbar is invisible, should be visible.\n");
2692 todo_wine {
2693 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2694 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2695 si.nPage, si.nMin, si.nMax);
2696 }
2697
2698 /* Ditto, see above */
2699 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2700 memset(&si, 0, sizeof(si));
2701 si.cbSize = sizeof(si);
2702 si.fMask = SIF_PAGE | SIF_RANGE;
2703 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2704 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2705 "Vertical scrollbar is invisible, should be visible.\n");
2706 todo_wine {
2707 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2708 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2709 si.nPage, si.nMin, si.nMax);
2710 }
2711
2712 /* Ditto, see above */
2713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719 "Vertical scrollbar is invisible, should be visible.\n");
2720 todo_wine {
2721 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2722 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2723 si.nPage, si.nMin, si.nMax);
2724 }
2725
2726 /* Ditto, see above */
2727 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2728 memset(&si, 0, sizeof(si));
2729 si.cbSize = sizeof(si);
2730 si.fMask = SIF_PAGE | SIF_RANGE;
2731 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2732 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2733 "Vertical scrollbar is invisible, should be visible.\n");
2734 todo_wine {
2735 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2736 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2737 si.nPage, si.nMin, si.nMax);
2738 }
2739
2740 /* Ditto, see above */
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 invisible, should be visible.\n");
2748 todo_wine {
2749 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751 si.nPage, si.nMin, si.nMax);
2752 }
2753
2754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2761 "Vertical scrollbar is visible, should be invisible.\n");
2762 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2763 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2764 si.nPage, si.nMin, si.nMax);
2765
2766 DestroyWindow(hwndRichEdit);
2767
2768 hwndRichEdit = new_richedit(NULL);
2769
2770 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2771 memset(&si, 0, sizeof(si));
2772 si.cbSize = sizeof(si);
2773 si.fMask = SIF_PAGE | SIF_RANGE;
2774 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2775 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2776 "Vertical scrollbar is visible, should be invisible.\n");
2777 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2778 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2779 si.nPage, si.nMin, si.nMax);
2780
2781 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2782 memset(&si, 0, sizeof(si));
2783 si.cbSize = sizeof(si);
2784 si.fMask = SIF_PAGE | SIF_RANGE;
2785 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2786 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2787 "Vertical scrollbar is visible, should be invisible.\n");
2788 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2789 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2790 si.nPage, si.nMin, si.nMax);
2791
2792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2793 memset(&si, 0, sizeof(si));
2794 si.cbSize = sizeof(si);
2795 si.fMask = SIF_PAGE | SIF_RANGE;
2796 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2797 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2798 "Vertical scrollbar is visible, should be invisible.\n");
2799 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2800 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2801 si.nPage, si.nMin, si.nMax);
2802
2803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809 "Vertical scrollbar is visible, should be invisible.\n");
2810 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2811 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2812 si.nPage, si.nMin, si.nMax);
2813
2814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2815 memset(&si, 0, sizeof(si));
2816 si.cbSize = sizeof(si);
2817 si.fMask = SIF_PAGE | SIF_RANGE;
2818 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2820 "Vertical scrollbar is invisible, should be visible.\n");
2821 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2822 "reported page/range is %d (%d..%d)\n",
2823 si.nPage, si.nMin, si.nMax);
2824
2825 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2826 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2836
2837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843 "Vertical scrollbar is visible, should be invisible.\n");
2844 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2845 "reported page/range is %d (%d..%d)\n",
2846 si.nPage, si.nMin, si.nMax);
2847
2848 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2849 EM_SCROLL will make visible any forcefully invisible scrollbar */
2850 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856 "Vertical scrollbar is invisible, should be visible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2860
2861 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2862 memset(&si, 0, sizeof(si));
2863 si.cbSize = sizeof(si);
2864 si.fMask = SIF_PAGE | SIF_RANGE;
2865 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2866 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2867 "Vertical scrollbar is visible, should be invisible.\n");
2868 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2869 "reported page/range is %d (%d..%d)\n",
2870 si.nPage, si.nMin, si.nMax);
2871
2872 /* Again, EM_SCROLL, with SB_LINEUP */
2873 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2879 "Vertical scrollbar is invisible, should be visible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2883
2884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890 "Vertical scrollbar is visible, should be invisible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2893 si.nPage, si.nMin, si.nMax);
2894
2895 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2901 "Vertical scrollbar is invisible, should be visible.\n");
2902 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903 "reported page/range is %d (%d..%d)\n",
2904 si.nPage, si.nMin, si.nMax);
2905
2906 DestroyWindow(hwndRichEdit);
2907
2908
2909 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2910 hwndRichEdit = new_richedit(NULL);
2911
2912 #define ENABLE_WS_VSCROLL(hwnd) \
2913 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2914 #define DISABLE_WS_VSCROLL(hwnd) \
2915 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2916
2917 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2918 ENABLE_WS_VSCROLL(hwndRichEdit);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2924 "Vertical scrollbar is invisible, should be visible.\n");
2925 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2926 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2927 si.nPage, si.nMin, si.nMax);
2928
2929 /* Ditto, see above */
2930 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2931 memset(&si, 0, sizeof(si));
2932 si.cbSize = sizeof(si);
2933 si.fMask = SIF_PAGE | SIF_RANGE;
2934 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936 "Vertical scrollbar is invisible, should be visible.\n");
2937 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939 si.nPage, si.nMin, si.nMax);
2940
2941 /* Ditto, see above */
2942 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2943 memset(&si, 0, sizeof(si));
2944 si.cbSize = sizeof(si);
2945 si.fMask = SIF_PAGE | SIF_RANGE;
2946 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948 "Vertical scrollbar is invisible, should be visible.\n");
2949 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951 si.nPage, si.nMin, si.nMax);
2952
2953 /* Ditto, see above */
2954 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2964
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2976
2977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2984 "Vertical scrollbar is visible, should be invisible.\n");
2985 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2987 si.nPage, si.nMin, si.nMax);
2988
2989 DestroyWindow(hwndRichEdit);
2990
2991 hwndRichEdit = new_richedit(NULL);
2992
2993 DISABLE_WS_VSCROLL(hwndRichEdit);
2994 memset(&si, 0, sizeof(si));
2995 si.cbSize = sizeof(si);
2996 si.fMask = SIF_PAGE | SIF_RANGE;
2997 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2998 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2999 "Vertical scrollbar is visible, should be invisible.\n");
3000 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3001 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3002 si.nPage, si.nMin, si.nMax);
3003
3004 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3005 memset(&si, 0, sizeof(si));
3006 si.cbSize = sizeof(si);
3007 si.fMask = SIF_PAGE | SIF_RANGE;
3008 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3009 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3010 "Vertical scrollbar is visible, should be invisible.\n");
3011 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3012 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3013 si.nPage, si.nMin, si.nMax);
3014
3015 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3016 memset(&si, 0, sizeof(si));
3017 si.cbSize = sizeof(si);
3018 si.fMask = SIF_PAGE | SIF_RANGE;
3019 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3020 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3021 "Vertical scrollbar is visible, should be invisible.\n");
3022 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3023 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3024 si.nPage, si.nMin, si.nMax);
3025
3026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3027 memset(&si, 0, sizeof(si));
3028 si.cbSize = sizeof(si);
3029 si.fMask = SIF_PAGE | SIF_RANGE;
3030 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3032 "Vertical scrollbar is visible, should be invisible.\n");
3033 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035 si.nPage, si.nMin, si.nMax);
3036
3037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3038 memset(&si, 0, sizeof(si));
3039 si.cbSize = sizeof(si);
3040 si.fMask = SIF_PAGE | SIF_RANGE;
3041 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3042 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3043 "Vertical scrollbar is invisible, should be visible.\n");
3044 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3045 "reported page/range is %d (%d..%d)\n",
3046 si.nPage, si.nMin, si.nMax);
3047
3048 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3049 DISABLE_WS_VSCROLL(hwndRichEdit);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057 "reported page/range is %d (%d..%d)\n",
3058 si.nPage, si.nMin, si.nMax);
3059
3060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066 "Vertical scrollbar is visible, should be invisible.\n");
3067 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3068 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3069 si.nPage, si.nMin, si.nMax);
3070
3071 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3072 memset(&si, 0, sizeof(si));
3073 si.cbSize = sizeof(si);
3074 si.fMask = SIF_PAGE | SIF_RANGE;
3075 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3077 "Vertical scrollbar is invisible, should be visible.\n");
3078 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3079 "reported page/range is %d (%d..%d)\n",
3080 si.nPage, si.nMin, si.nMax);
3081
3082 DISABLE_WS_VSCROLL(hwndRichEdit);
3083 memset(&si, 0, sizeof(si));
3084 si.cbSize = sizeof(si);
3085 si.fMask = SIF_PAGE | SIF_RANGE;
3086 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088 "Vertical scrollbar is visible, should be invisible.\n");
3089 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3090 "reported page/range is %d (%d..%d)\n",
3091 si.nPage, si.nMin, si.nMax);
3092
3093 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3094 EM_SCROLL will make visible any forcefully invisible scrollbar */
3095 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3096 memset(&si, 0, sizeof(si));
3097 si.cbSize = sizeof(si);
3098 si.fMask = SIF_PAGE | SIF_RANGE;
3099 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3100 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3101 "Vertical scrollbar is invisible, should be visible.\n");
3102 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3103 "reported page/range is %d (%d..%d)\n",
3104 si.nPage, si.nMin, si.nMax);
3105
3106 DISABLE_WS_VSCROLL(hwndRichEdit);
3107 memset(&si, 0, sizeof(si));
3108 si.cbSize = sizeof(si);
3109 si.fMask = SIF_PAGE | SIF_RANGE;
3110 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3112 "Vertical scrollbar is visible, should be invisible.\n");
3113 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3114 "reported page/range is %d (%d..%d)\n",
3115 si.nPage, si.nMin, si.nMax);
3116
3117 /* Again, EM_SCROLL, with SB_LINEUP */
3118 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124 "Vertical scrollbar is invisible, should be visible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3128
3129 DestroyWindow(hwndRichEdit);
3130
3131 /* This window proc models what is going on with Corman Lisp 3.0.
3132 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3133 force the scrollbar into visibility. Recursion should NOT happen
3134 as a result of this action.
3135 */
3136 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3137 if (r) {
3138 richeditProc = cls.lpfnWndProc;
3139 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3140 cls.lpszClassName = "RicheditStupidOverride";
3141 if(!RegisterClassA(&cls)) assert(0);
3142
3143 recursionLevel = 0;
3144 WM_SIZE_recursionLevel = 0;
3145 bailedOutOfRecursion = FALSE;
3146 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3147 ok(!bailedOutOfRecursion,
3148 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3149
3150 recursionLevel = 0;
3151 WM_SIZE_recursionLevel = 0;
3152 bailedOutOfRecursion = FALSE;
3153 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3154 ok(!bailedOutOfRecursion,
3155 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3156
3157 /* Unblock window in order to process WM_DESTROY */
3158 recursionLevel = 0;
3159 bailedOutOfRecursion = FALSE;
3160 WM_SIZE_recursionLevel = 0;
3161 DestroyWindow(hwndRichEdit);
3162 }
3163 }
3164
3165 static void test_EM_SETUNDOLIMIT(void)
3166 {
3167 /* cases we test for:
3168 * default behaviour - limiting at 100 undo's
3169 * undo disabled - setting a limit of 0
3170 * undo limited - undo limit set to some to some number, like 2
3171 * bad input - sending a negative number should default to 100 undo's */
3172
3173 HWND hwndRichEdit = new_richedit(NULL);
3174 CHARRANGE cr;
3175 int i;
3176 int result;
3177
3178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3179 cr.cpMin = 0;
3180 cr.cpMax = 1;
3181 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3182 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3183 also, multiple pastes don't combine like WM_CHAR would */
3184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3185
3186 /* first case - check the default */
3187 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3188 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3189 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3190 for (i=0; i<100; i++) /* Undo 100 of them */
3191 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3192 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3193 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3194
3195 /* second case - cannot undo */
3196 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3197 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3198 SendMessage(hwndRichEdit,
3199 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3200 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3201 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3202
3203 /* third case - set it to an arbitrary number */
3204 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3205 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3206 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3207 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3208 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3209 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3210 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3211 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3212 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3213 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3214 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3215 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3216 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3217 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3218
3219 /* fourth case - setting negative numbers should default to 100 undos */
3220 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3221 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3222 ok (result == 100,
3223 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3224
3225 DestroyWindow(hwndRichEdit);
3226 }
3227
3228 static void test_ES_PASSWORD(void)
3229 {
3230 /* This isn't hugely testable, so we're just going to run it through its paces */
3231
3232 HWND hwndRichEdit = new_richedit(NULL);
3233 WCHAR result;
3234
3235 /* First, check the default of a regular control */
3236 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3237 ok (result == 0,
3238 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3239
3240 /* Now, set it to something normal */
3241 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3242 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3243 ok (result == 120,
3244 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3245
3246 /* Now, set it to something odd */
3247 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3248 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3249 ok (result == 1234,
3250 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3251 DestroyWindow(hwndRichEdit);
3252 }
3253
3254 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3255 LPBYTE pbBuff,
3256 LONG cb,
3257 LONG *pcb)
3258 {
3259 char** str = (char**)dwCookie;
3260 *pcb = cb;
3261 if (*pcb > 0) {
3262 memcpy(*str, pbBuff, *pcb);
3263 *str += *pcb;
3264 }
3265 return 0;
3266 }
3267
3268 static void test_WM_SETTEXT(void)
3269 {
3270 HWND hwndRichEdit = new_richedit(NULL);
3271 const char * TestItem1 = "TestSomeText";
3272 const char * TestItem2 = "TestSomeText\r";
3273 const char * TestItem2_after = "TestSomeText\r\n";
3274 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3275 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3276 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3277 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3278 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3279 const char * TestItem5_after = "TestSomeText TestSomeText";
3280 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3281 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3282 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3283 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3284
3285 char buf[1024] = {0};
3286 LRESULT result;
3287 EDITSTREAM es;
3288 char * p;
3289
3290 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3291 any solitary \r to be converted to \r\n on return. Properly paired
3292 \r\n are not affected. It also shows that the special sequence \r\r\n
3293 gets converted to a single space.
3294 */
3295
3296 #define TEST_SETTEXT(a, b) \
3297 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3298 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3299 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3300 ok (result == lstrlen(buf), \
3301 "WM_GETTEXT returned %ld instead of expected %u\n", \
3302 result, lstrlen(buf)); \
3303 result = strcmp(b, buf); \
3304 ok(result == 0, \
3305 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3306
3307 TEST_SETTEXT(TestItem1, TestItem1)
3308 TEST_SETTEXT(TestItem2, TestItem2_after)
3309 TEST_SETTEXT(TestItem3, TestItem3_after)
3310 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3311 TEST_SETTEXT(TestItem4, TestItem4_after)
3312 TEST_SETTEXT(TestItem5, TestItem5_after)
3313 TEST_SETTEXT(TestItem6, TestItem6_after)
3314 TEST_SETTEXT(TestItem7, TestItem7_after)
3315
3316 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
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_RTF), (LPARAM)&es);
3325 trace("EM_STREAMOUT produced: \n%s\n", buf);
3326 TEST_SETTEXT(buf, TestItem1)
3327
3328 #undef TEST_SETTEXT
3329 DestroyWindow(hwndRichEdit);
3330 }
3331
3332 static void test_EM_STREAMOUT(void)
3333 {
3334 HWND hwndRichEdit = new_richedit(NULL);
3335 int r;
3336 EDITSTREAM es;
3337 char buf[1024] = {0};
3338 char * p;
3339
3340 const char * TestItem1 = "TestSomeText";
3341 const char * TestItem2 = "TestSomeText\r";
3342 const char * TestItem3 = "TestSomeText\r\n";
3343
3344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3345 p = buf;
3346 es.dwCookie = (DWORD_PTR)&p;
3347 es.dwError = 0;
3348 es.pfnCallback = test_WM_SETTEXT_esCallback;
3349 memset(buf, 0, sizeof(buf));
3350 SendMessage(hwndRichEdit, EM_STREAMOUT,
3351 (WPARAM)(SF_TEXT), (LPARAM)&es);
3352 r = strlen(buf);
3353 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3354 ok(strcmp(buf, TestItem1) == 0,
3355 "streamed text different, got %s\n", buf);
3356
3357 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3358 p = buf;
3359 es.dwCookie = (DWORD_PTR)&p;
3360 es.dwError = 0;
3361 es.pfnCallback = test_WM_SETTEXT_esCallback;
3362 memset(buf, 0, sizeof(buf));
3363 SendMessage(hwndRichEdit, EM_STREAMOUT,
3364 (WPARAM)(SF_TEXT), (LPARAM)&es);
3365 r = strlen(buf);
3366 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3367 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3368 ok(strcmp(buf, TestItem3) == 0,
3369 "streamed text different from, got %s\n", buf);
3370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3371 p = buf;
3372 es.dwCookie = (DWORD_PTR)&p;
3373 es.dwError = 0;
3374 es.pfnCallback = test_WM_SETTEXT_esCallback;
3375 memset(buf, 0, sizeof(buf));
3376 SendMessage(hwndRichEdit, EM_STREAMOUT,
3377 (WPARAM)(SF_TEXT), (LPARAM)&es);
3378 r = strlen(buf);
3379 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3380 ok(strcmp(buf, TestItem3) == 0,
3381 "streamed text different, got %s\n", buf);
3382
3383 DestroyWindow(hwndRichEdit);
3384 }
3385
3386 static void test_EM_STREAMOUT_FONTTBL(void)
3387 {
3388 HWND hwndRichEdit = new_richedit(NULL);
3389 EDITSTREAM es;
3390 char buf[1024] = {0};
3391 char * p;
3392 char * fontTbl;
3393 int brackCount;
3394
3395 const char * TestItem = "TestSomeText";
3396
3397 /* fills in the richedit control with some text */
3398 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3399
3400 /* streams out the text in rtf format */
3401 p = buf;
3402 es.dwCookie = (DWORD_PTR)&p;
3403 es.dwError = 0;
3404 es.pfnCallback = test_WM_SETTEXT_esCallback;
3405 memset(buf, 0, sizeof(buf));
3406 SendMessage(hwndRichEdit, EM_STREAMOUT,
3407 (WPARAM)(SF_RTF), (LPARAM)&es);
3408
3409 /* scans for \fonttbl, error if not found */
3410 fontTbl = strstr(buf, "\\fonttbl");
3411 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3412 if(fontTbl)
3413 {
3414 /* scans for terminating closing bracket */
3415 brackCount = 1;
3416 while(*fontTbl && brackCount)
3417 {
3418 if(*fontTbl == '{')
3419 brackCount++;
3420 else if(*fontTbl == '}')
3421 brackCount--;
3422 fontTbl++;
3423 }
3424 /* checks whether closing bracket is ok */
3425 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3426 if(!brackCount)
3427 {
3428 /* char before closing fonttbl block should be a closed bracket */
3429 fontTbl -= 2;
3430 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3431
3432 /* char after fonttbl block should be a crlf */
3433 fontTbl += 2;
3434 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3435 }
3436 }
3437 DestroyWindow(hwndRichEdit);
3438 }
3439
3440
3441 static void test_EM_SETTEXTEX(void)
3442 {
3443 HWND hwndRichEdit, parent;
3444 SCROLLINFO si;
3445 int sel_start, sel_end;
3446 SETTEXTEX setText;
3447 GETTEXTEX getText;
3448 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3449 'S', 'o', 'm', 'e',
3450 'T', 'e', 'x', 't', 0};
3451 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3452 't', 'S', 'o', 'm',
3453 'e', 'T', 'e', 'x',
3454 't', 't', 'S', 'o',
3455 'm', 'e', 'T', 'e',
3456 'x', 't', 0};
3457 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3458 '\r','t','S','o','m','e','T','e','x','t',0};
3459 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3460 'S', 'o', 'm', 'e',
3461 'T', 'e', 'x', 't',
3462 '\r', 0};
3463 const char * TestItem2_after = "TestSomeText\r\n";
3464 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3465 'S', 'o', 'm', 'e',
3466 'T', 'e', 'x', 't',
3467 '\r','\n','\r','\n', 0};
3468 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3469 'S', 'o', 'm', 'e',
3470 'T', 'e', 'x', 't',
3471 '\n','\n', 0};
3472 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3473 'S', 'o', 'm', 'e',
3474 'T', 'e', 'x', 't',
3475 '\r','\r', 0};
3476 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3477 'S', 'o', 'm', 'e',
3478 'T', 'e', 'x', 't',
3479 '\r','\r','\n','\r',
3480 '\n', 0};
3481 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3482 'S', 'o', 'm', 'e',
3483 'T', 'e', 'x', 't',
3484 ' ','\r', 0};
3485 #define MAX_BUF_LEN 1024
3486 WCHAR buf[MAX_BUF_LEN];
3487 char bufACP[MAX_BUF_LEN];
3488 char * p;
3489 int result;
3490 CHARRANGE cr;
3491 EDITSTREAM es;
3492 WNDCLASSA cls;
3493
3494 /* Test the scroll position with and without a parent window.
3495 *
3496 * For some reason the scroll position is 0 after EM_SETTEXTEX
3497 * with the ST_SELECTION flag only when the control has a parent
3498 * window, even though the selection is at the end. */
3499 cls.style = 0;
3500 cls.lpfnWndProc = DefWindowProcA;
3501 cls.cbClsExtra = 0;
3502 cls.cbWndExtra = 0;
3503 cls.hInstance = GetModuleHandleA(0);
3504 cls.hIcon = 0;
3505 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3506 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3507 cls.lpszMenuName = NULL;
3508 cls.lpszClassName = "ParentTestClass";
3509 if(!RegisterClassA(&cls)) assert(0);
3510
3511 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3512 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3513 ok (parent != 0, "Failed to create parent window\n");
3514
3515 hwndRichEdit = CreateWindowEx(0,
3516 RICHEDIT_CLASS, NULL,
3517 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3518 0, 0, 200, 60, parent, NULL,
3519 hmoduleRichEdit, NULL);
3520
3521 setText.codepage = CP_ACP;
3522 setText.flags = ST_SELECTION;
3523 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3524 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3525 si.cbSize = sizeof(si);
3526 si.fMask = SIF_ALL;
3527 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3528 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3529 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3530 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3531 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3532
3533 DestroyWindow(parent);
3534
3535 /* Test without a parent window */
3536 hwndRichEdit = new_richedit(NULL);
3537 setText.codepage = CP_ACP;
3538 setText.flags = ST_SELECTION;
3539 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3540 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3541 si.cbSize = sizeof(si);
3542 si.fMask = SIF_ALL;
3543 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3544 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3545 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3546 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3547 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3548
3549 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3550 * but this time it is because the selection is at the beginning. */
3551 setText.codepage = CP_ACP;
3552 setText.flags = ST_DEFAULT;
3553 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3554 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3555 si.cbSize = sizeof(si);
3556 si.fMask = SIF_ALL;
3557 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3558 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3559 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3560 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3561 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3562
3563 setText.codepage = 1200; /* no constant for unicode */
3564 getText.codepage = 1200; /* no constant for unicode */
3565 getText.cb = MAX_BUF_LEN;
3566 getText.flags = GT_DEFAULT;
3567 getText.lpDefaultChar = NULL;
3568 getText.lpUsedDefChar = NULL;
3569
3570 setText.flags = 0;
3571 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3572 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3573 ok(lstrcmpW(buf, TestItem1) == 0,
3574 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3575
3576 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3577 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3578 */
3579 setText.codepage = 1200; /* no constant for unicode */
3580 getText.codepage = 1200; /* no constant for unicode */
3581 getText.cb = MAX_BUF_LEN;
3582 getText.flags = GT_DEFAULT;
3583 getText.lpDefaultChar = NULL;
3584 getText.lpUsedDefChar = NULL;
3585 setText.flags = 0;
3586 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3587 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3588 ok(lstrcmpW(buf, TestItem2) == 0,
3589 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3590
3591 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3592 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3593 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3594 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3595
3596 /* Baseline test for just-enough buffer space for string */
3597 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3598 getText.codepage = 1200; /* no constant for unicode */
3599 getText.flags = GT_DEFAULT;
3600 getText.lpDefaultChar = NULL;
3601 getText.lpUsedDefChar = NULL;
3602 memset(buf, 0, MAX_BUF_LEN);
3603 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3604 ok(lstrcmpW(buf, TestItem2) == 0,
3605 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3606
3607 /* When there is enough space for one character, but not both, of the CRLF
3608 pair at the end of the string, the CR is not copied at all. That is,
3609 the caller must not see CRLF pairs truncated to CR at the end of the
3610 string.
3611 */
3612 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3613 getText.codepage = 1200; /* no constant for unicode */
3614 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3615 getText.lpDefaultChar = NULL;
3616 getText.lpUsedDefChar = NULL;
3617 memset(buf, 0, MAX_BUF_LEN);
3618 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3619 ok(lstrcmpW(buf, TestItem1) == 0,
3620 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3621
3622
3623 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3624 setText.codepage = 1200; /* no constant for unicode */
3625 getText.codepage = 1200; /* no constant for unicode */
3626 getText.cb = MAX_BUF_LEN;
3627 getText.flags = GT_DEFAULT;
3628 getText.lpDefaultChar = NULL;
3629 getText.lpUsedDefChar = NULL;
3630 setText.flags = 0;
3631 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3632 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3633 ok(lstrcmpW(buf, TestItem3_after) == 0,
3634 "EM_SETTEXTEX did not convert properly\n");
3635
3636 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3637 setText.codepage = 1200; /* no constant for unicode */
3638 getText.codepage = 1200; /* no constant for unicode */
3639 getText.cb = MAX_BUF_LEN;
3640 getText.flags = GT_DEFAULT;
3641 getText.lpDefaultChar = NULL;
3642 getText.lpUsedDefChar = NULL;
3643 setText.flags = 0;
3644 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3645 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3646 ok(lstrcmpW(buf, TestItem3_after) == 0,
3647 "EM_SETTEXTEX did not convert properly\n");
3648
3649 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3650 setText.codepage = 1200; /* no constant for unicode */
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.cb = MAX_BUF_LEN;
3653 getText.flags = GT_DEFAULT;
3654 getText.lpDefaultChar = NULL;
3655 getText.lpUsedDefChar = NULL;
3656 setText.flags = 0;
3657 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3658 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3659 ok(lstrcmpW(buf, TestItem4_after) == 0,
3660 "EM_SETTEXTEX did not convert properly\n");
3661
3662 /* !ST_SELECTION && Unicode && !\rtf */
3663 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3664 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3665
3666 ok (result == 1,
3667 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3668 ok(lstrlenW(buf) == 0,
3669 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3670
3671 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3672 setText.flags = 0;
3673 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3674 /* select some text */
3675 cr.cpMax = 1;
3676 cr.cpMin = 3;
3677 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3678 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3679 setText.flags = ST_SELECTION;
3680 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3681 ok(result == 0,
3682 "EM_SETTEXTEX with NULL lParam to replace selection"
3683 " with no text should return 0. Got %i\n",
3684 result);
3685
3686 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3687 setText.flags = 0;
3688 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3689 /* select some text */
3690 cr.cpMax = 1;
3691 cr.cpMin = 3;
3692 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3693 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3694 setText.flags = ST_SELECTION;
3695 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3696 (WPARAM)&setText, (LPARAM) TestItem1);
3697 /* get text */
3698 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3699 ok(result == lstrlenW(TestItem1),
3700 "EM_SETTEXTEX with NULL lParam to replace selection"
3701 " with no text should return 0. Got %i\n",
3702 result);
3703 ok(lstrlenW(buf) == 22,
3704 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3705 lstrlenW(buf) );
3706
3707 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3708 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3709 p = (char *)buf;
3710 es.dwCookie = (DWORD_PTR)&p;
3711 es.dwError = 0;
3712 es.pfnCallback = test_WM_SETTEXT_esCallback;
3713 memset(buf, 0, sizeof(buf));
3714 SendMessage(hwndRichEdit, EM_STREAMOUT,
3715 (WPARAM)(SF_RTF), (LPARAM)&es);
3716 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3717
3718 /* !ST_SELECTION && !Unicode && \rtf */
3719 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3720 getText.codepage = 1200; /* no constant for unicode */
3721 getText.cb = MAX_BUF_LEN;
3722 getText.flags = GT_DEFAULT;
3723 getText.lpDefaultChar = NULL;
3724 getText.lpUsedDefChar = NULL;
3725
3726 setText.flags = 0;
3727 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3728 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3729 ok(lstrcmpW(buf, TestItem1) == 0,
3730 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3731
3732 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3733 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3734 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3735 getText.codepage = CP_ACP;
3736 getText.cb = MAX_BUF_LEN;
3737 getText.flags = GT_DEFAULT;
3738 getText.lpDefaultChar = NULL;
3739 getText.lpUsedDefChar = NULL;
3740
3741 setText.flags = ST_SELECTION;
3742 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3743 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3744 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3745 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3746 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3747
3748 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3749 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3750 p = (char *)buf;
3751 es.dwCookie = (DWORD_PTR)&p;
3752 es.dwError = 0;
3753 es.pfnCallback = test_WM_SETTEXT_esCallback;
3754 memset(buf, 0, sizeof(buf));
3755 SendMessage(hwndRichEdit, EM_STREAMOUT,
3756 (WPARAM)(SF_RTF), (LPARAM)&es);
3757 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3758
3759 /* select some text */
3760 cr.cpMax = 1;
3761 cr.cpMin = 3;
3762 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3763
3764 /* ST_SELECTION && !Unicode && \rtf */
3765 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3766 getText.codepage = 1200; /* no constant for unicode */
3767 getText.cb = MAX_BUF_LEN;
3768 getText.flags = GT_DEFAULT;
3769 getText.lpDefaultChar = NULL;
3770 getText.lpUsedDefChar = NULL;
3771
3772 setText.flags = ST_SELECTION;
3773 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3774 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3775 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3776
3777 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3778 setText.codepage = 1200; /* no constant for unicode */
3779 getText.codepage = CP_ACP;
3780 getText.cb = MAX_BUF_LEN;
3781
3782 setText.flags = 0;
3783 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3784 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3785
3786 /* select some text */
3787 cr.cpMax = 1;
3788 cr.cpMin = 3;
3789 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3790
3791 /* ST_SELECTION && !Unicode && !\rtf */
3792 setText.codepage = CP_ACP;
3793 getText.codepage = 1200; /* no constant for unicode */
3794 getText.cb = MAX_BUF_LEN;
3795 getText.flags = GT_DEFAULT;
3796 getText.lpDefaultChar = NULL;
3797 getText.lpUsedDefChar = NULL;
3798
3799 setText.flags = ST_SELECTION;
3800 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3801 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3802 ok(lstrcmpW(buf, TestItem1alt) == 0,
3803 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3804 " using ST_SELECTION and non-Unicode\n");
3805
3806 /* Test setting text using rich text format */
3807 setText.flags = 0;
3808 setText.codepage = CP_ACP;
3809 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3810 getText.codepage = CP_ACP;
3811 getText.cb = MAX_BUF_LEN;
3812 getText.flags = GT_DEFAULT;
3813 getText.lpDefaultChar = NULL;
3814 getText.lpUsedDefChar = NULL;
3815 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3816 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3817
3818 setText.flags = 0;
3819 setText.codepage = CP_ACP;
3820 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3821 getText.codepage = CP_ACP;
3822 getText.cb = MAX_BUF_LEN;
3823 getText.flags = GT_DEFAULT;
3824 getText.lpDefaultChar = NULL;
3825 getText.lpUsedDefChar = NULL;
3826 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3827 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3828
3829 DestroyWindow(hwndRichEdit);
3830 }
3831
3832 static void test_EM_LIMITTEXT(void)
3833 {
3834 int ret;
3835
3836 HWND hwndRichEdit = new_richedit(NULL);
3837
3838 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3839 * about setting the length to -1 for multiline edit controls doesn't happen.
3840 */
3841
3842 /* Don't check default gettextlimit case. That's done in other tests */
3843
3844 /* Set textlimit to 100 */
3845 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3846 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3847 ok (ret == 100,
3848 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3849
3850 /* Set textlimit to 0 */
3851 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3852 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3853 ok (ret == 65536,
3854 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3855
3856 /* Set textlimit to -1 */
3857 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3858 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3859 ok (ret == -1,
3860 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3861
3862 /* Set textlimit to -2 */
3863 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3864 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3865 ok (ret == -2,
3866 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3867
3868 DestroyWindow (hwndRichEdit);
3869 }
3870
3871
3872 static void test_EM_EXLIMITTEXT(void)
3873 {
3874 int i, selBegin, selEnd, len1, len2;
3875 int result;
3876 char text[1024 + 1];
3877 char buffer[1024 + 1];
3878 int textlimit = 0; /* multiple of 100 */
3879 HWND hwndRichEdit = new_richedit(NULL);
3880
3881 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3882 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3883
3884 textlimit = 256000;
3885 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3886 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3887 /* set higher */
3888 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3889
3890 textlimit = 1000;
3891 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3892 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3893 /* set lower */
3894 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3895
3896 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3897 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3898 /* default for WParam = 0 */
3899 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3900
3901 textlimit = sizeof(text)-1;
3902 memset(text, 'W', textlimit);
3903 text[sizeof(text)-1] = 0;
3904 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3905 /* maxed out text */
3906 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3907
3908 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3909 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3910 len1 = selEnd - selBegin;
3911
3912 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3913 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3914 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3915 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3916 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3917 len2 = selEnd - selBegin;
3918
3919 ok(len1 != len2,
3920 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3921 len1,len2,i);
3922
3923 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3924 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3925 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3926 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3927 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3928 len1 = selEnd - selBegin;
3929
3930 ok(len1 != len2,
3931 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3932 len1,len2,i);
3933
3934 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3935 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3936 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3937 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3938 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3939 len2 = selEnd - selBegin;
3940
3941 ok(len1 == len2,
3942 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3943 len1,len2,i);
3944
3945 /* set text up to the limit, select all the text, then add a char */
3946 textlimit = 5;
3947 memset(text, 'W', textlimit);
3948 text[textlimit] = 0;
3949 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3950 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3951 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3952 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3953 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3954 result = strcmp(buffer, "A");
3955 ok(0 == result, "got string = \"%s\"\n", buffer);
3956
3957 /* WM_SETTEXT not limited */
3958 textlimit = 10;
3959 memset(text, 'W', textlimit);
3960 text[textlimit] = 0;
3961 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3962 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3963 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3964 i = strlen(buffer);
3965 ok(10 == i, "expected 10 chars\n");
3966 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3967 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3968
3969 /* try inserting more text at end */
3970 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3971 ok(0 == i, "WM_CHAR wasn't processed\n");
3972 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3973 i = strlen(buffer);
3974 ok(10 == i, "expected 10 chars, got %i\n", i);
3975 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3976 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3977
3978 /* try inserting text at beginning */
3979 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3980 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3981 ok(0 == i, "WM_CHAR wasn't processed\n");
3982 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3983 i = strlen(buffer);
3984 ok(10 == i, "expected 10 chars, got %i\n", i);
3985 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3986 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3987
3988 /* WM_CHAR is limited */
3989 textlimit = 1;
3990 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3991 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3992 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3993 ok(0 == i, "WM_CHAR wasn't processed\n");
3994 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3995 ok(0 == i, "WM_CHAR wasn't processed\n");
3996 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3997 i = strlen(buffer);
3998 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3999
4000 DestroyWindow(hwndRichEdit);
4001 }
4002
4003 static void test_EM_GETLIMITTEXT(void)
4004 {
4005 int i;
4006 HWND hwndRichEdit = new_richedit(NULL);
4007
4008 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4009 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4010
4011 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4012 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4013 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4014
4015 DestroyWindow(hwndRichEdit);
4016 }
4017
4018 static void test_WM_SETFONT(void)
4019 {
4020 /* There is no invalid input or error conditions for this function.
4021 * NULL wParam and lParam just fall back to their default values
4022 * It should be noted that even if you use a gibberish name for your fonts
4023 * here, it will still work because the name is stored. They will display as
4024 * System, but will report their name to be whatever they were created as */
4025
4026 HWND hwndRichEdit = new_richedit(NULL);
4027 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4028 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4029 FF_DONTCARE, "Marlett");
4030 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4031 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4032 FF_DONTCARE, "MS Sans Serif");
4033 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4034 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4035 FF_DONTCARE, "Courier");
4036 LOGFONTA sentLogFont;
4037 CHARFORMAT2A returnedCF2A;
4038
4039 returnedCF2A.cbSize = sizeof(returnedCF2A);
4040
4041 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4042 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4043 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4044
4045 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4046 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4047 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4048 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4049
4050 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4051 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4052 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4053 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4054 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4055 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4056
4057 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4058 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4059 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4060 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4061 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4062 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4063
4064 /* This last test is special since we send in NULL. We clear the variables
4065 * and just compare to "System" instead of the sent in font name. */
4066 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4067 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4068 returnedCF2A.cbSize = sizeof(returnedCF2A);
4069
4070 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4071 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4072 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4073 ok (!strcmp("System",returnedCF2A.szFaceName),
4074 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4075
4076 DestroyWindow(hwndRichEdit);
4077 }
4078
4079
4080 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4081 LPBYTE pbBuff,
4082 LONG cb,
4083 LONG *pcb)
4084 {
4085 const char** str = (const char**)dwCookie;
4086 int size = strlen(*str);
4087 if(size > 3) /* let's make it piecemeal for fun */
4088 size = 3;
4089 *pcb = cb;
4090 if (*pcb > size) {
4091 *pcb = size;
4092 }
4093 if (*pcb > 0) {
4094 memcpy(pbBuff, *str, *pcb);
4095 *str += *pcb;
4096 }
4097 return 0;
4098 }
4099
4100 static void test_EM_GETMODIFY(void)
4101 {
4102 HWND hwndRichEdit = new_richedit(NULL);
4103 LRESULT result;
4104 SETTEXTEX setText;
4105 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4106 'S', 'o', 'm', 'e',
4107 'T', 'e', 'x', 't', 0};
4108 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4109 'S', 'o', 'm', 'e',
4110 'O', 't', 'h', 'e', 'r',
4111 'T', 'e', 'x', 't', 0};
4112 const char* streamText = "hello world";
4113 CHARFORMAT2 cf2;
4114 PARAFORMAT2 pf2;
4115 EDITSTREAM es;
4116
4117 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4118 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4119 FF_DONTCARE, "Courier");
4120
4121 setText.codepage = 1200; /* no constant for unicode */
4122 setText.flags = ST_KEEPUNDO;
4123
4124
4125 /* modify flag shouldn't be set when richedit is first created */
4126 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4127 ok (result == 0,
4128 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4129
4130 /* setting modify flag should actually set it */
4131 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4132 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4133 ok (result != 0,
4134 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4135
4136 /* clearing modify flag should actually clear it */
4137 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4138 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4139 ok (result == 0,
4140 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4141
4142 /* setting font doesn't change modify flag */
4143 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4144 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4145 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4146 ok (result == 0,
4147 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4148
4149 /* setting text should set modify flag */
4150 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4151 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4152 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4153 ok (result != 0,
4154 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4155
4156 /* undo previous text doesn't reset modify flag */
4157 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4158 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4159 ok (result != 0,
4160 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4161
4162 /* set text with no flag to keep undo stack should not set modify flag */
4163 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4164 setText.flags = 0;
4165 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4166 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4167 ok (result == 0,
4168 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4169
4170 /* WM_SETTEXT doesn't modify */
4171 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4173 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4174 ok (result == 0,
4175 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4176
4177 /* clear the text */
4178 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4179 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4180 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4181 ok (result == 0,
4182 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4183
4184 /* replace text */
4185 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4186 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4187 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4188 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4189 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4190 ok (result != 0,
4191 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4192
4193 /* copy/paste text 1 */
4194 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4195 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4196 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4197 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4198 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4199 ok (result != 0,
4200 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4201
4202 /* copy/paste text 2 */
4203 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4204 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4205 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4206 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4207 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4208 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4209 ok (result != 0,
4210 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4211
4212 /* press char */
4213 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4214 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4215 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4216 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4217 ok (result != 0,
4218 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4219
4220 /* press del */
4221 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4222 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4223 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4224 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4225 ok (result != 0,
4226 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4227
4228 /* set char format */
4229 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 cf2.cbSize = sizeof(CHARFORMAT2);
4231 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4232 (LPARAM) &cf2);
4233 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4234 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4235 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4236 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4237 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4238 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4239 ok (result != 0,
4240 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4241
4242 /* set para format */
4243 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4244 pf2.cbSize = sizeof(PARAFORMAT2);
4245 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4246 (LPARAM) &pf2);
4247 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4248 pf2.wAlignment = PFA_RIGHT;
4249 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4250 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4251 ok (result == 0,
4252 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4253
4254 /* EM_STREAM */
4255 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4256 es.dwCookie = (DWORD_PTR)&streamText;
4257 es.dwError = 0;
4258 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4259 SendMessage(hwndRichEdit, EM_STREAMIN,
4260 (WPARAM)(SF_TEXT), (LPARAM)&es);
4261 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4262 ok (result != 0,
4263 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4264
4265 DestroyWindow(hwndRichEdit);
4266 }
4267
4268 struct exsetsel_s {
4269 LONG min;
4270 LONG max;
4271 LRESULT expected_retval;
4272 int expected_getsel_start;
4273 int expected_getsel_end;
4274 int _getsel_todo_wine;
4275 };
4276
4277 const struct exsetsel_s exsetsel_tests[] = {
4278 /* sanity tests */
4279 {5, 10, 10, 5, 10, 0},
4280 {15, 17, 17, 15, 17, 0},
4281 /* test cpMax > strlen() */
4282 {0, 100, 18, 0, 18, 1},
4283 /* test cpMin == cpMax */
4284 {5, 5, 5, 5, 5, 0},
4285 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4286 {-1, 0, 5, 5, 5, 0},
4287 {-1, 17, 5, 5, 5, 0},
4288 {-1, 18, 5, 5, 5, 0},
4289 /* test cpMin < 0 && cpMax < 0 */
4290 {-1, -1, 17, 17, 17, 0},
4291 {-4, -5, 17, 17, 17, 0},
4292 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4293 {0, -1, 18, 0, 18, 1},
4294 {17, -5, 18, 17, 18, 1},
4295 {18, -3, 17, 17, 17, 0},
4296 /* test if cpMin > cpMax */
4297 {15, 19, 18, 15, 18, 1},
4298 {19, 15, 18, 15, 18, 1}
4299 };
4300
4301 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4302 CHARRANGE cr;
4303 long result;
4304 int start, end;
4305
4306 cr.cpMin = setsel->min;
4307 cr.cpMax = setsel->max;
4308 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4309
4310 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4311
4312 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4313
4314 if (setsel->_getsel_todo_wine) {
4315 todo_wine {
4316 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);
4317 }
4318 } else {
4319 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);
4320 }
4321 }
4322
4323 static void test_EM_EXSETSEL(void)
4324 {
4325 HWND hwndRichEdit = new_richedit(NULL);
4326 int i;
4327 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4328
4329 /* sending some text to the window */
4330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4331 /* 01234567890123456*/
4332 /* 10 */
4333
4334 for (i = 0; i < num_tests; i++) {
4335 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4336 }
4337
4338 DestroyWindow(hwndRichEdit);
4339 }
4340
4341 static void test_EM_REPLACESEL(int redraw)
4342 {
4343 HWND hwndRichEdit = new_richedit(NULL);
4344 char buffer[1024] = {0};
4345 int r;
4346 GETTEXTEX getText;
4347 CHARRANGE cr;
4348
4349 /* sending some text to the window */
4350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4351 /* 01234567890123456*/
4352 /* 10 */
4353
4354 /* FIXME add more tests */
4355 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4356 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4357 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4358 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4359 r = strcmp(buffer, "testing");
4360 ok(0 == r, "expected %d, got %d\n", 0, r);
4361
4362 DestroyWindow(hwndRichEdit);
4363
4364 hwndRichEdit = new_richedit(NULL);
4365
4366 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4367 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4368
4369 /* Test behavior with carriage returns and newlines */
4370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4371 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4372 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4373 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4374 r = strcmp(buffer, "RichEdit1");
4375 ok(0 == r, "expected %d, got %d\n", 0, r);
4376 getText.cb = 1024;
4377 getText.codepage = CP_ACP;
4378 getText.flags = GT_DEFAULT;
4379 getText.lpDefaultChar = NULL;
4380 getText.lpUsedDefChar = NULL;
4381 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4382 ok(strcmp(buffer, "RichEdit1") == 0,
4383 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4384
4385 /* Test number of lines reported after EM_REPLACESEL */
4386 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4387 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4388
4389 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4390 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4391 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4392 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4393 r = strcmp(buffer, "RichEdit1\r\n");
4394 ok(0 == r, "expected %d, got %d\n", 0, r);
4395 getText.cb = 1024;
4396 getText.codepage = CP_ACP;
4397 getText.flags = GT_DEFAULT;
4398 getText.lpDefaultChar = NULL;
4399 getText.lpUsedDefChar = NULL;
4400 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4401 ok(strcmp(buffer, "RichEdit1\r") == 0,
4402 "EM_GETTEXTEX returned incorrect string\n");
4403
4404 /* Test number of lines reported after EM_REPLACESEL */
4405 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4406 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4407
4408 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4409 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4410 returns the number of characters *inserted* into the control (after
4411 required conversions), but WinXP's riched20 returns the number of
4412 characters interpreted from the original lParam. Wine's builtin riched20
4413 implements the WinXP behavior.
4414 */
4415 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4416 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4417 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4418 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4419
4420 /* Test number of lines reported after EM_REPLACESEL */
4421 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4422 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4423
4424 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4425 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4426 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4427 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4428
4429 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4430 r = strcmp(buffer, "RichEdit1\r\n");
4431 ok(0 == r, "expected %d, got %d\n", 0, r);
4432 getText.cb = 1024;
4433 getText.codepage = CP_ACP;
4434 getText.flags = GT_DEFAULT;
4435 getText.lpDefaultChar = NULL;
4436 getText.lpUsedDefChar = NULL;
4437 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4438 ok(strcmp(buffer, "RichEdit1\r") == 0,
4439 "EM_GETTEXTEX returned incorrect string\n");
4440
4441 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4442 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4443 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4444 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4445
4446 /* The following tests show that richedit should handle the special \r\r\n
4447 sequence by turning it into a single space on insertion. However,
4448 EM_REPLACESEL on WinXP returns the number of characters in the original
4449 string.
4450 */
4451
4452 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4453 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4454 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4455 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4456 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4457 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4458 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4459
4460 /* Test the actual string */
4461 getText.cb = 1024;
4462 getText.codepage = CP_ACP;
4463 getText.flags = GT_DEFAULT;
4464 getText.lpDefaultChar = NULL;
4465 getText.lpUsedDefChar = NULL;
4466 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4467 ok(strcmp(buffer, "\r\r") == 0,
4468 "EM_GETTEXTEX returned incorrect string\n");
4469
4470 /* Test number of lines reported after EM_REPLACESEL */
4471 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4472 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4473
4474 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4475 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4476 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4477 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4478 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4479 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4480 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4481 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4482
4483 /* Test the actual string */
4484 getText.cb = 1024;
4485 getText.codepage = CP_ACP;
4486 getText.flags = GT_DEFAULT;
4487 getText.lpDefaultChar = NULL;
4488 getText.lpUsedDefChar = NULL;
4489 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4490 ok(strcmp(buffer, " ") == 0,
4491 "EM_GETTEXTEX returned incorrect string\n");
4492
4493 /* Test number of lines reported after EM_REPLACESEL */
4494 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4495 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4496
4497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4498 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4499 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4500 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4501 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4502 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4503 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4504 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4505
4506 /* Test the actual string */
4507 getText.cb = 1024;
4508 getText.codepage = CP_ACP;
4509 getText.flags = GT_DEFAULT;
4510 getText.lpDefaultChar = NULL;
4511 getText.lpUsedDefChar = NULL;
4512 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4513 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4514 "EM_GETTEXTEX returned incorrect string\n");
4515
4516 /* Test number of lines reported after EM_REPLACESEL */
4517 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4518 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4519
4520 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4521 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4522 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4523 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4524 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4525 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4526 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4527 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4528
4529 /* Test the actual string */
4530 getText.cb = 1024;
4531 getText.codepage = CP_ACP;
4532 getText.flags = GT_DEFAULT;
4533 getText.lpDefaultChar = NULL;
4534 getText.lpUsedDefChar = NULL;
4535 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4536 ok(strcmp(buffer, " \r") == 0,
4537 "EM_GETTEXTEX returned incorrect string\n");
4538
4539 /* Test number of lines reported after EM_REPLACESEL */
4540 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4541 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4542
4543 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4544 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4545 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4546 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4547 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4548 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4549 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4550 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4551
4552 /* Test the actual string */
4553 getText.cb = 1024;
4554 getText.codepage = CP_ACP;
4555 getText.flags = GT_DEFAULT;
4556 getText.lpDefaultChar = NULL;
4557 getText.lpUsedDefChar = NULL;
4558 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4559 ok(strcmp(buffer, " \r\r") == 0,
4560 "EM_GETTEXTEX returned incorrect string\n");
4561
4562 /* Test number of lines reported after EM_REPLACESEL */
4563 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4564 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4565
4566 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4567 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4568 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4569 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4570 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4571 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4572 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4573 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4574
4575 /* Test the actual string */
4576 getText.cb = 1024;
4577 getText.codepage = CP_ACP;
4578 getText.flags = GT_DEFAULT;
4579 getText.lpDefaultChar = NULL;
4580 getText.lpUsedDefChar = NULL;
4581 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4582 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4583 "EM_GETTEXTEX returned incorrect string\n");
4584
4585 /* Test number of lines reported after EM_REPLACESEL */
4586 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4587 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4588
4589 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4590 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4591 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4592 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4593 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4594 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4595 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4596
4597 /* Test the actual string */
4598 getText.cb = 1024;
4599 getText.codepage = CP_ACP;
4600 getText.flags = GT_DEFAULT;
4601 getText.lpDefaultChar = NULL;
4602 getText.lpUsedDefChar = NULL;
4603 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4604 ok(strcmp(buffer, "\r\r") == 0,
4605 "EM_GETTEXTEX returned incorrect string\n");
4606
4607 /* Test number of lines reported after EM_REPLACESEL */
4608 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4609 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4610
4611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4612 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4613 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4614 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4615 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4616 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4617 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4618 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4619
4620 /* Test the actual string */
4621 getText.cb = 1024;
4622 getText.codepage = CP_ACP;
4623 getText.flags = GT_DEFAULT;
4624 getText.lpDefaultChar = NULL;
4625 getText.lpUsedDefChar = NULL;
4626 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4627 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4628 "EM_GETTEXTEX returned incorrect string\n");
4629
4630 /* Test number of lines reported after EM_REPLACESEL */
4631 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4632 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4633
4634 if (!redraw)
4635 /* This is needed to avoid interferring with keybd_event calls
4636 * on other tests that simulate keyboard events. */
4637 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4638
4639 DestroyWindow(hwndRichEdit);
4640 }
4641
4642 static void test_WM_PASTE(void)
4643 {
4644 int result;
4645 char buffer[1024] = {0};
4646 const char* text1 = "testing paste\r";
4647 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4648 const char* text1_after = "testing paste\r\n";
4649 const char* text2 = "testing paste\r\rtesting paste";
4650 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4651 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4652 HWND hwndRichEdit = new_richedit(NULL);
4653
4654 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4655 * to test the state of the modifiers (Ctrl/Alt/Shift).
4656 *
4657 * Therefore Ctrl-<key> keystrokes need to be simulated with
4658 * keybd_event or by using SetKeyboardState to set the modifiers
4659 * and SendMessage to simulate the keystrokes.
4660 */
4661
4662 /* Sent keystrokes with keybd_event */
4663 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4664 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4665 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4666 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4667 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4668
4669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4670 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4671
4672 SEND_CTRL_C(hwndRichEdit); /* Copy */
4673 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4674 SEND_CTRL_V(hwndRichEdit); /* Paste */
4675 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4676 /* Pasted text should be visible at this step */
4677 result = strcmp(text1_step1, buffer);
4678 ok(result == 0,
4679 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4680
4681 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4683 /* Text should be the same as before (except for \r -> \r\n conversion) */
4684 result = strcmp(text1_after, buffer);
4685 ok(result == 0,
4686 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4687
4688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4689 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4690 SEND_CTRL_C(hwndRichEdit); /* Copy */
4691 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4692 SEND_CTRL_V(hwndRichEdit); /* Paste */
4693 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4694 /* Pasted text should be visible at this step */
4695 result = strcmp(text3, buffer);
4696 ok(result == 0,
4697 "test paste: strcmp = %i\n", result);
4698 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4699 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4700 /* Text should be the same as before (except for \r -> \r\n conversion) */
4701 result = strcmp(text2_after, buffer);
4702 ok(result == 0,
4703 "test paste: strcmp = %i\n", result);
4704 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4705 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4706 /* Text should revert to post-paste state */
4707 result = strcmp(buffer,text3);
4708 ok(result == 0,
4709 "test paste: strcmp = %i\n", result);
4710
4711 #undef SEND_CTRL_C
4712 #undef SEND_CTRL_X
4713 #undef SEND_CTRL_V
4714 #undef SEND_CTRL_Z
4715 #undef SEND_CTRL_Y
4716
4717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4718 /* Send WM_CHAR to simulates Ctrl-V */
4719 SendMessage(hwndRichEdit, WM_CHAR, 22,
4720 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4721 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4722 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4723 result = strcmp(buffer,"");
4724 ok(result == 0,
4725 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4726
4727 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4728 * with SetKeyboard state. */
4729
4730 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4731 /* Simulates paste (Ctrl-V) */
4732 hold_key(VK_CONTROL);
4733 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4734 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4735 release_key(VK_CONTROL);
4736 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4737 result = strcmp(buffer,"paste");
4738 ok(result == 0,
4739 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4740
4741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4742 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4743 /* Simulates copy (Ctrl-C) */
4744 hold_key(VK_CONTROL);
4745 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4746 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4747 release_key(VK_CONTROL);
4748 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4749 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4750 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4751 result = strcmp(buffer,"testing");
4752 ok(result == 0,
4753 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4754
4755 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4757 /* Simulates select all (Ctrl-A) */
4758 hold_key(VK_CONTROL);
4759 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4760 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4761 /* Simulates select cut (Ctrl-X) */
4762 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4763 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4764 release_key(VK_CONTROL);
4765 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4766 result = strcmp(buffer,"");
4767 ok(result == 0,
4768 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4769 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4770 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 result = strcmp(buffer,"cut\r\n");
4773 todo_wine ok(result == 0,
4774 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4775 /* Simulates undo (Ctrl-Z) */
4776 hold_key(VK_CONTROL);
4777 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4778 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4779 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4780 result = strcmp(buffer,"");
4781 ok(result == 0,
4782 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4783 /* Simulates redo (Ctrl-Y) */
4784 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4785 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4786 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4787 result = strcmp(buffer,"cut\r\n");
4788 todo_wine ok(result == 0,
4789 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4790 release_key(VK_CONTROL);
4791
4792 DestroyWindow(hwndRichEdit);
4793 }
4794
4795 static void test_EM_FORMATRANGE(void)
4796 {
4797 int i, tpp_x, tpp_y;
4798 HDC hdc;
4799 HWND hwndRichEdit = new_richedit(NULL);
4800 static const struct {
4801 const char *string; /* The string */
4802 int first; /* First 'pagebreak', 0 for don't care */
4803 int second; /* Second 'pagebreak', 0 for don't care */
4804 } fmtstrings[] = {
4805 {"WINE wine", 0, 0},
4806 {"WINE wineWine", 0, 0},
4807 {"WINE\r\nwine\r\nwine", 5, 10},
4808 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4809 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4810 };
4811
4812 hdc = GetDC(hwndRichEdit);
4813 ok(hdc != NULL, "Could not get HDC\n");
4814
4815 /* Calculate the twips per pixel */
4816 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4817 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4818
4819 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4820
4821 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4822 {
4823 FORMATRANGE fr;
4824 GETTEXTLENGTHEX gtl;
4825 SIZE stringsize;
4826 int r, len;
4827
4828 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4829
4830 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4831 gtl.codepage = CP_ACP;
4832 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4833
4834 /* Get some size information for the string */
4835 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4836
4837 /* Define the box to be half the width needed and a bit larger than the height.
4838 * Changes to the width means we have at least 2 pages. Changes to the height
4839 * is done so we can check the changing of fr.rc.bottom.
4840 */
4841 fr.hdc = fr.hdcTarget = hdc;
4842 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4843 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4844 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4845
4846 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4847 todo_wine {
4848 ok(r == len, "Expected %d, got %d\n", len, r);
4849 }
4850
4851 /* We know that the page can't hold the full string. See how many characters
4852 * are on the first one
4853 */
4854 fr.chrg.cpMin = 0;
4855 fr.chrg.cpMax = -1;
4856 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4857 todo_wine {
4858 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4859 }
4860 if (fmtstrings[i].first)
4861 todo_wine {
4862 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4863 }
4864 else
4865 ok(r < len, "Expected < %d, got %d\n", len, r);
4866
4867 /* Do another page */
4868 fr.chrg.cpMin = r;
4869 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4870 if (fmtstrings[i].second)
4871 todo_wine {
4872 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4873 }
4874 else
4875 ok (r < len, "Expected < %d, got %d\n", len, r);
4876
4877 /* There is at least on more page, but we don't care */
4878
4879 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4880 todo_wine {
4881 ok(r == len, "Expected %d, got %d\n", len, r);
4882 }
4883 }
4884
4885 ReleaseDC(NULL, hdc);
4886 DestroyWindow(hwndRichEdit);
4887 }
4888
4889 static int nCallbackCount = 0;
4890
4891 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4892 LONG cb, LONG* pcb)
4893 {
4894 const char text[] = {'t','e','s','t'};
4895
4896 if (sizeof(text) <= cb)
4897 {
4898 if ((int)dwCookie != nCallbackCount)
4899 {
4900 *pcb = 0;
4901 return 0;
4902 }
4903
4904 memcpy (pbBuff, text, sizeof(text));
4905 *pcb = sizeof(text);
4906
4907 nCallbackCount++;
4908
4909 return 0;
4910 }
4911 else
4912 return 1; /* indicates callback failed */
4913 }
4914
4915 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4916 LPBYTE pbBuff,
4917 LONG cb,
4918 LONG *pcb)
4919 {
4920 const char** str = (const char**)dwCookie;
4921 int size = strlen(*str);
4922 *pcb = cb;
4923 if (*pcb > size) {
4924 *pcb = size;
4925 }
4926 if (*pcb > 0) {
4927 memcpy(pbBuff, *str, *pcb);
4928 *str += *pcb;
4929 }
4930 return 0;
4931 }
4932
4933 struct StringWithLength {
4934 int length;
4935 char *buffer;
4936 };
4937
4938 /* This callback is used to handled the null characters in a string. */
4939 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4940 LPBYTE pbBuff,
4941 LONG cb,
4942 LONG *pcb)
4943 {
4944 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4945 int size = str->length;
4946 *pcb = cb;
4947 if (*pcb > size) {
4948 *pcb = size;
4949 }
4950 if (*pcb > 0) {
4951 memcpy(pbBuff, str->buffer, *pcb);
4952 str->buffer += *pcb;
4953 str->length -= *pcb;
4954 }
4955 return 0;
4956 }
4957
4958 static void test_EM_STREAMIN(void)
4959 {
4960 HWND hwndRichEdit = new_richedit(NULL);
4961 LRESULT result;
4962 EDITSTREAM es;
4963 char buffer[1024] = {0};
4964
4965 const char * streamText0 = "{\\rtf1 TestSomeText}";
4966 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4967 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4968
4969 const char * streamText1 =
4970 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4971 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4972 "}\r\n";
4973
4974 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4975 const char * streamText2 =
4976 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4977 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4978 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4979 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4980 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4981 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4982 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4983
4984 const char * streamText3 = "RichEdit1";
4985
4986 struct StringWithLength cookieForStream4;
4987 const char * streamText4 =
4988 "This text just needs to be long enough to cause run to be split onto "
4989 "two separate lines and make sure the null terminating character is "
4990 "handled properly.\0";
4991 int length4 = strlen(streamText4) + 1;
4992 cookieForStream4.buffer = (char *)streamText4;
4993 cookieForStream4.length = length4;
4994
4995 /* Minimal test without \par at the end */
4996 es.dwCookie = (DWORD_PTR)&streamText0;
4997 es.dwError = 0;
4998 es.pfnCallback = test_EM_STREAMIN_esCallback;
4999 SendMessage(hwndRichEdit, EM_STREAMIN,
5000 (WPARAM)(SF_RTF), (LPARAM)&es);
5001
5002 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5003 ok (result == 12,
5004 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5005 result = strcmp (buffer,"TestSomeText");
5006 ok (result == 0,
5007 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5008 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5009
5010 /* Native richedit 2.0 ignores last \par */
5011 es.dwCookie = (DWORD_PTR)&streamText0a;
5012 es.dwError = 0;
5013 es.pfnCallback = test_EM_STREAMIN_esCallback;
5014 SendMessage(hwndRichEdit, EM_STREAMIN,
5015 (WPARAM)(SF_RTF), (LPARAM)&es);
5016
5017 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5018 ok (result == 12,
5019 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5020 result = strcmp (buffer,"TestSomeText");
5021 ok (result == 0,
5022 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5023 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5024
5025 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5026 es.dwCookie = (DWORD_PTR)&streamText0b;
5027 es.dwError = 0;
5028 es.pfnCallback = test_EM_STREAMIN_esCallback;
5029 SendMessage(hwndRichEdit, EM_STREAMIN,
5030 (WPARAM)(SF_RTF), (LPARAM)&es);
5031
5032 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5033 ok (result == 14,
5034 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5035 result = strcmp (buffer,"TestSomeText\r\n");
5036 ok (result == 0,
5037 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5038 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5039
5040 es.dwCookie = (DWORD_PTR)&streamText1;
5041 es.dwError = 0;
5042 es.pfnCallback = test_EM_STREAMIN_esCallback;
5043 SendMessage(hwndRichEdit, EM_STREAMIN,
5044 (WPARAM)(SF_RTF), (LPARAM)&es);
5045
5046 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5047 ok (result == 12,
5048 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5049 result = strcmp (buffer,"TestSomeText");
5050 ok (result == 0,
5051 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5052 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5053
5054 es.dwCookie = (DWORD_PTR)&streamText2;
5055 es.dwError = 0;
5056 SendMessage(hwndRichEdit, EM_STREAMIN,
5057 (WPARAM)(SF_RTF), (LPARAM)&es);
5058
5059 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5060 ok (result == 0,
5061 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5062 ok (strlen(buffer) == 0,
5063 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5064 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5065
5066 es.dwCookie = (DWORD_PTR)&streamText3;
5067 es.dwError = 0;
5068 SendMessage(hwndRichEdit, EM_STREAMIN,
5069 (WPARAM)(SF_RTF), (LPARAM)&es);
5070
5071 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5072 ok (result == 0,
5073 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5074 ok (strlen(buffer) == 0,
5075 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5076 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5077
5078 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5079 es.dwError = 0;
5080 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5081 SendMessage(hwndRichEdit, EM_STREAMIN,
5082 (WPARAM)(SF_TEXT), (LPARAM)&es);
5083
5084 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5085 ok (result == length4,
5086 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5087 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5088
5089 DestroyWindow(hwndRichEdit);
5090 }
5091
5092 static void test_EM_StreamIn_Undo(void)
5093 {
5094 /* The purpose of this test is to determine when a EM_StreamIn should be
5095 * undoable. This is important because WM_PASTE currently uses StreamIn and
5096 * pasting should always be undoable but streaming isn't always.
5097 *
5098 * cases to test:
5099 * StreamIn plain text without SFF_SELECTION.
5100 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5101 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5102 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5103 * Feel free to add tests for other text modes or StreamIn things.
5104 */
5105
5106
5107 HWND hwndRichEdit = new_richedit(NULL);
5108 LRESULT result;
5109 EDITSTREAM es;
5110 char buffer[1024] = {0};
5111 const char randomtext[] = "Some text";
5112
5113 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5114
5115 /* StreamIn, no SFF_SELECTION */
5116 es.dwCookie = nCallbackCount;
5117 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5118 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5119 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5120 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
5121 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5122 result = strcmp (buffer,"test");
5123 ok (result == 0,
5124 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5125
5126 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5127 ok (result == FALSE,
5128 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5129
5130 /* StreamIn, SFF_SELECTION, but nothing selected */
5131 es.dwCookie = nCallbackCount;
5132 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5133 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5134 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5135 SendMessage(hwndRichEdit, EM_STREAMIN,
5136 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5137 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5138 result = strcmp (buffer,"testSome text");
5139 ok (result == 0,
5140 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5141
5142 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5143 ok (result == TRUE,
5144 "EM_STREAMIN with SFF_SELECTION but no selection set "
5145 "should create an undo\n");
5146
5147 /* StreamIn, SFF_SELECTION, with a selection */
5148 es.dwCookie = nCallbackCount;
5149 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5150 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5151 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5152 SendMessage(hwndRichEdit, EM_STREAMIN,
5153 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5154 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5155 result = strcmp (buffer,"Sometesttext");
5156 ok (result == 0,
5157 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5158
5159 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5160 ok (result == TRUE,
5161 "EM_STREAMIN with SFF_SELECTION and selection set "
5162 "should create an undo\n");
5163
5164 DestroyWindow(hwndRichEdit);
5165 }
5166
5167 static BOOL is_em_settextex_supported(HWND hwnd)
5168 {
5169 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5170 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5171 }
5172
5173 static void test_unicode_conversions(void)
5174 {
5175 static const WCHAR tW[] = {'t',0};
5176 static const WCHAR teW[] = {'t','e',0};
5177 static const WCHAR textW[] = {'t','e','s','t',0};
5178 static const char textA[] = "test";
5179 char bufA[64];
5180 WCHAR bufW[64];
5181 HWND hwnd;
5182 int em_settextex_supported, ret;
5183
5184 #define set_textA(hwnd, wm_set_text, txt) \
5185 do { \
5186 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5187 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5188 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5189 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5190 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5191 } while(0)
5192 #define expect_textA(hwnd, wm_get_text, txt) \
5193 do { \
5194 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5195 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5196 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5197 memset(bufA, 0xAA, sizeof(bufA)); \
5198 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5199 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5200 ret = lstrcmpA(bufA, txt); \
5201 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5202 } while(0)
5203
5204 #define set_textW(hwnd, wm_set_text, txt) \
5205 do { \
5206 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5207 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5208 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5209 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5210 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5211 } while(0)
5212 #define expect_textW(hwnd, wm_get_text, txt) \
5213 do { \
5214 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5215 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5216 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5217 memset(bufW, 0xAA, sizeof(bufW)); \
5218 if (is_win9x) \
5219 { \
5220 assert(wm_get_text == EM_GETTEXTEX); \
5221 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5222 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5223 } \
5224 else \
5225 { \
5226 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5227 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5228 } \
5229 ret = lstrcmpW(bufW, txt); \
5230 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5231 } while(0)
5232 #define expect_empty(hwnd, wm_get_text) \
5233 do { \
5234 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5235 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5236 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5237 memset(bufA, 0xAA, sizeof(bufA)); \
5238 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5239 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5240 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5241 } while(0)
5242
5243 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5244 0, 0, 200, 60, 0, 0, 0, 0);
5245 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5246
5247 ret = IsWindowUnicode(hwnd);
5248 if (is_win9x)
5249 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5250 else
5251 ok(ret, "RichEdit20W should be unicode under NT\n");
5252
5253 /* EM_SETTEXTEX is supported starting from version 3.0 */
5254 em_settextex_supported = is_em_settextex_supported(hwnd);
5255 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5256 em_settextex_supported ? "" : "NOT ");
5257
5258 expect_empty(hwnd, WM_GETTEXT);
5259 expect_empty(hwnd, EM_GETTEXTEX);
5260
5261 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5262 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5263 expect_textA(hwnd, WM_GETTEXT, "t");
5264 expect_textA(hwnd, EM_GETTEXTEX, "t");
5265 expect_textW(hwnd, EM_GETTEXTEX, tW);
5266
5267 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5268 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5269 expect_textA(hwnd, WM_GETTEXT, "te");
5270 expect_textA(hwnd, EM_GETTEXTEX, "te");
5271 expect_textW(hwnd, EM_GETTEXTEX, teW);
5272
5273 set_textA(hwnd, WM_SETTEXT, NULL);
5274 expect_empty(hwnd, WM_GETTEXT);
5275 expect_empty(hwnd, EM_GETTEXTEX);
5276
5277 if (is_win9x)
5278 set_textA(hwnd, WM_SETTEXT, textW);
5279 else
5280 set_textA(hwnd, WM_SETTEXT, textA);
5281 expect_textA(hwnd, WM_GETTEXT, textA);
5282 expect_textA(hwnd, EM_GETTEXTEX, textA);
5283 expect_textW(hwnd, EM_GETTEXTEX, textW);
5284
5285 if (em_settextex_supported)
5286 {
5287 set_textA(hwnd, EM_SETTEXTEX, textA);
5288 expect_textA(hwnd, WM_GETTEXT, textA);
5289 expect_textA(hwnd, EM_GETTEXTEX, textA);
5290 expect_textW(hwnd, EM_GETTEXTEX, textW);
5291 }
5292
5293 if (!is_win9x)
5294 {
5295 set_textW(hwnd, WM_SETTEXT, textW);
5296 expect_textW(hwnd, WM_GETTEXT, textW);
5297 expect_textA(hwnd, WM_GETTEXT, textA);
5298 expect_textW(hwnd, EM_GETTEXTEX, textW);
5299 expect_textA(hwnd, EM_GETTEXTEX, textA);
5300
5301 if (em_settextex_supported)
5302 {
5303 set_textW(hwnd, EM_SETTEXTEX, textW);
5304 expect_textW(hwnd, WM_GETTEXT, textW);
5305 expect_textA(hwnd, WM_GETTEXT, textA);
5306 expect_textW(hwnd, EM_GETTEXTEX, textW);
5307 expect_textA(hwnd, EM_GETTEXTEX, textA);
5308 }
5309 }
5310 DestroyWindow(hwnd);
5311
5312 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5313 0, 0, 200, 60, 0, 0, 0, 0);
5314 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5315
5316 ret = IsWindowUnicode(hwnd);
5317 ok(!ret, "RichEdit20A should NOT be unicode\n");
5318
5319 set_textA(hwnd, WM_SETTEXT, textA);
5320 expect_textA(hwnd, WM_GETTEXT, textA);
5321 expect_textA(hwnd, EM_GETTEXTEX, textA);
5322 expect_textW(hwnd, EM_GETTEXTEX, textW);
5323
5324 if (em_settextex_supported)
5325 {
5326 set_textA(hwnd, EM_SETTEXTEX, textA);
5327 expect_textA(hwnd, WM_GETTEXT, textA);
5328 expect_textA(hwnd, EM_GETTEXTEX, textA);
5329 expect_textW(hwnd, EM_GETTEXTEX, textW);
5330 }
5331
5332 if (!is_win9x)
5333 {
5334 set_textW(hwnd, WM_SETTEXT, textW);
5335 expect_textW(hwnd, WM_GETTEXT, textW);
5336 expect_textA(hwnd, WM_GETTEXT, textA);
5337 expect_textW(hwnd, EM_GETTEXTEX, textW);
5338 expect_textA(hwnd, EM_GETTEXTEX, textA);
5339
5340 if (em_settextex_supported)
5341 {
5342 set_textW(hwnd, EM_SETTEXTEX, textW);
5343 expect_textW(hwnd, WM_GETTEXT, textW);
5344 expect_textA(hwnd, WM_GETTEXT, textA);
5345 expect_textW(hwnd, EM_GETTEXTEX, textW);
5346 expect_textA(hwnd, EM_GETTEXTEX, textA);
5347 }
5348 }
5349 DestroyWindow(hwnd);
5350 }
5351
5352 static void test_WM_CHAR(void)
5353 {
5354 HWND hwnd;
5355 int ret;
5356 const char * char_list = "abc\rabc\r";
5357 const char * expected_content_single = "abcabc";
5358 const char * expected_content_multi = "abc\r\nabc\r\n";
5359 char buffer[64] = {0};
5360 const char * p;
5361
5362 /* single-line control must IGNORE carriage returns */
5363 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5364 0, 0, 200, 60, 0, 0, 0, 0);
5365 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5366
5367 p = char_list;
5368 while (*p != '\0') {
5369 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5370 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5371 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5372 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5373 p++;
5374 }
5375
5376 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5377 ret = strcmp(buffer, expected_content_single);
5378 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5379
5380 DestroyWindow(hwnd);
5381
5382 /* multi-line control inserts CR normally */
5383 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5384 0, 0, 200, 60, 0, 0, 0, 0);
5385 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5386
5387 p = char_list;
5388 while (*p != '\0') {
5389 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5390 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5391 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5392 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5393 p++;
5394 }
5395
5396 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5397 ret = strcmp(buffer, expected_content_multi);
5398 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5399
5400 DestroyWindow(hwnd);
5401 }
5402
5403 static void test_EM_GETTEXTLENGTHEX(void)
5404 {
5405 HWND hwnd;
5406 GETTEXTLENGTHEX gtl;
5407 int ret;
5408 const char * base_string = "base string";
5409 const char * test_string = "a\nb\n\n\r\n";
5410 const char * test_string_after = "a";
5411 const char * test_string_2 = "a\rtest\rstring";
5412 char buffer[64] = {0};
5413
5414 /* single line */
5415 if (!is_win9x)
5416 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5417 0, 0, 200, 60, 0, 0, 0, 0);
5418 else
5419 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5420 0, 0, 200, 60, 0, 0, 0, 0);
5421 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5422
5423 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5424 gtl.codepage = CP_ACP;
5425 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5426 ok(ret == 0, "ret %d\n",ret);
5427
5428 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5429 gtl.codepage = CP_ACP;
5430 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5431 ok(ret == 0, "ret %d\n",ret);
5432
5433 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5434
5435 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5436 gtl.codepage = CP_ACP;
5437 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5438 ok(ret == strlen(base_string), "ret %d\n",ret);
5439
5440 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5441 gtl.codepage = CP_ACP;
5442 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5443 ok(ret == strlen(base_string), "ret %d\n",ret);
5444
5445 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5446
5447 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5448 gtl.codepage = CP_ACP;
5449 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5450 ok(ret == 1, "ret %d\n",ret);
5451
5452 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5453 gtl.codepage = CP_ACP;
5454 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5455 ok(ret == 1, "ret %d\n",ret);
5456
5457 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5458 ret = strcmp(buffer, test_string_after);
5459 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5460
5461 DestroyWindow(hwnd);
5462
5463 /* multi line */
5464 if (!is_win9x)
5465 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5466 0, 0, 200, 60, 0, 0, 0, 0);
5467 else
5468 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5469 0, 0, 200, 60, 0, 0, 0, 0);
5470 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5471
5472 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5473 gtl.codepage = CP_ACP;
5474 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5475 ok(ret == 0, "ret %d\n",ret);
5476
5477 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5478 gtl.codepage = CP_ACP;
5479 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5480 ok(ret == 0, "ret %d\n",ret);
5481
5482 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5483
5484 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5485 gtl.codepage = CP_ACP;
5486 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5487 ok(ret == strlen(base_string), "ret %d\n",ret);
5488
5489 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5490 gtl.codepage = CP_ACP;
5491 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5492 ok(ret == strlen(base_string), "ret %d\n",ret);
5493
5494 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5495
5496 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5497 gtl.codepage = CP_ACP;
5498 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5499 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5500
5501 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5502 gtl.codepage = CP_ACP;
5503 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5504 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5505
5506 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5507
5508 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5509 gtl.codepage = CP_ACP;
5510 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5511 ok(ret == 10, "ret %d\n",ret);
5512
5513 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5514 gtl.codepage = CP_ACP;
5515 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5516 ok(ret == 6, "ret %d\n",ret);
5517
5518 DestroyWindow(hwnd);
5519 }
5520
5521
5522 /* globals that parent and child access when checking event masks & notifications */
5523 static HWND eventMaskEditHwnd = 0;
5524 static int queriedEventMask;
5525 static int watchForEventMask = 0;
5526
5527 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5528 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5529 {
5530 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5531 {
5532 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5533 }
5534 return DefWindowProcA(hwnd, message, wParam, lParam);
5535 }
5536
5537 /* test event masks in combination with WM_COMMAND */
5538 static void test_eventMask(void)
5539 {
5540 HWND parent;
5541 int ret, style;
5542 WNDCLASSA cls;
5543 const char text[] = "foo bar\n";
5544 int eventMask;
5545
5546 /* register class to capture WM_COMMAND */
5547 cls.style = 0;
5548 cls.lpfnWndProc = ParentMsgCheckProcA;
5549 cls.cbClsExtra = 0;
5550 cls.cbWndExtra = 0;
5551 cls.hInstance = GetModuleHandleA(0);
5552 cls.hIcon = 0;
5553 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5554 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5555 cls.lpszMenuName = NULL;
5556 cls.lpszClassName = "EventMaskParentClass";
5557 if(!RegisterClassA(&cls)) assert(0);
5558
5559 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5560 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5561 ok (parent != 0, "Failed to create parent window\n");
5562
5563 eventMaskEditHwnd = new_richedit(parent);
5564 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5565
5566 eventMask = ENM_CHANGE | ENM_UPDATE;
5567 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5568 ok(ret == ENM_NONE, "wrong event mask\n");
5569 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5570 ok(ret == eventMask, "failed to set event mask\n");
5571
5572 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5573 queriedEventMask = 0; /* initialize to something other than we expect */
5574 watchForEventMask = EN_CHANGE;
5575 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5576 ok(ret == TRUE, "failed to set text\n");
5577 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5578 notification in response to WM_SETTEXT */
5579 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5580 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5581
5582 /* check to see if EN_CHANGE is sent when redraw is turned off */
5583 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5584 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5585 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5586 /* redraw is disabled by making the window invisible. */
5587 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5588 queriedEventMask = 0; /* initialize to something other than we expect */
5589 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5590 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5591 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5592 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5593 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5594
5595 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5596 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5597 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5598 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5599 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5600 watchForEventMask = EN_UPDATE;
5601 queriedEventMask = 0; /* initialize to something other than we expect */
5602 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5603 ok(queriedEventMask == 0,
5604 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5605 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5606 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5607 queriedEventMask = 0; /* initialize to something other than we expect */
5608 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5609 ok(queriedEventMask == eventMask,
5610 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5611
5612
5613 DestroyWindow(parent);
5614 }
5615
5616 static int received_WM_NOTIFY = 0;
5617 static int modify_at_WM_NOTIFY = 0;
5618 static HWND hwndRichedit_WM_NOTIFY;
5619
5620 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5621 {
5622 if(message == WM_NOTIFY)
5623 {
5624 received_WM_NOTIFY = 1;
5625 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5626 }
5627 return DefWindowProcA(hwnd, message, wParam, lParam);
5628 }
5629
5630 static void test_WM_NOTIFY(void)
5631 {
5632 HWND parent;
5633 WNDCLASSA cls;
5634 CHARFORMAT2 cf2;
5635
5636 /* register class to capture WM_NOTIFY */
5637 cls.style = 0;
5638 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5639 cls.cbClsExtra = 0;
5640 cls.cbWndExtra = 0;
5641 cls.hInstance = GetModuleHandleA(0);
5642 cls.hIcon = 0;
5643 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5644 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5645 cls.lpszMenuName = NULL;
5646 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5647 if(!RegisterClassA(&cls)) assert(0);
5648
5649 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5650 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5651 ok (parent != 0, "Failed to create parent window\n");
5652
5653 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5654 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5655
5656 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5657
5658 /* Notifications for selection change should only be sent when selection
5659 actually changes. EM_SETCHARFORMAT is one message that calls
5660 ME_CommitUndo, which should check whether message should be sent */
5661 received_WM_NOTIFY = 0;
5662 cf2.cbSize = sizeof(CHARFORMAT2);
5663 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5664 (LPARAM) &cf2);
5665 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5666 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5667 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5668 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5669
5670 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5671 already at 0. */
5672 received_WM_NOTIFY = 0;
5673 modify_at_WM_NOTIFY = 0;
5674 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5675 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5676 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5677
5678 received_WM_NOTIFY = 0;
5679 modify_at_WM_NOTIFY = 0;
5680 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5681 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5682
5683 received_WM_NOTIFY = 0;
5684 modify_at_WM_NOTIFY = 0;
5685 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5686 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5687 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5688
5689 /* Test for WM_NOTIFY messages with redraw disabled. */
5690 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5691 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5692 received_WM_NOTIFY = 0;
5693 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5694 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5695 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5696
5697 DestroyWindow(hwndRichedit_WM_NOTIFY);
5698 DestroyWindow(parent);
5699 }
5700
5701 static void test_undo_coalescing(void)
5702 {
5703 HWND hwnd;
5704 int result;
5705 char buffer[64] = {0};
5706
5707 /* multi-line control inserts CR normally */
5708 if (!is_win9x)
5709 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5710 0, 0, 200, 60, 0, 0, 0, 0);
5711 else
5712 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5713 0, 0, 200, 60, 0, 0, 0, 0);
5714 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5715
5716 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5717 ok (result == FALSE, "Can undo after window creation.\n");
5718 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5719 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5720 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5721 ok (result == FALSE, "Can redo after window creation.\n");
5722 result = SendMessage(hwnd, EM_REDO, 0, 0);
5723 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5724
5725 /* Test the effect of arrows keys during typing on undo transactions*/
5726 simulate_typing_characters(hwnd, "one two three");
5727 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5728 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5729 simulate_typing_characters(hwnd, " four five six");
5730
5731 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5732 ok (result == FALSE, "Can redo before anything is undone.\n");
5733 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5734 ok (result == TRUE, "Cannot undo typed characters.\n");
5735 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5736 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5737 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5738 ok (result == TRUE, "Cannot redo after undo.\n");
5739 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5740 result = strcmp(buffer, "one two three");
5741 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5742
5743 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5744 ok (result == TRUE, "Cannot undo typed characters.\n");
5745 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5746 ok (result == TRUE, "Failed to undo typed characters.\n");
5747 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5748 result = strcmp(buffer, "");
5749 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5750
5751 /* Test the effect of focus changes during typing on undo transactions*/
5752 simulate_typing_characters(hwnd, "one two three");
5753 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5754 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5755 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5756 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5757 simulate_typing_characters(hwnd, " four five six");
5758 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5759 ok (result == TRUE, "Failed to undo typed characters.\n");
5760 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5761 result = strcmp(buffer, "one two three");
5762 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5763
5764 /* Test the effect of the back key during typing on undo transactions */
5765 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5766 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5767 ok (result == TRUE, "Failed to clear the text.\n");
5768 simulate_typing_characters(hwnd, "one two threa");
5769 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5770 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5771 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5772 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5773 simulate_typing_characters(hwnd, "e four five six");
5774 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5775 ok (result == TRUE, "Failed to undo typed characters.\n");
5776 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5777 result = strcmp(buffer, "");
5778 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5779
5780 /* Test the effect of the delete key during typing on undo transactions */
5781 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5782 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5783 ok(result == TRUE, "Failed to set the text.\n");
5784 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5785 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5786 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5787 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5788 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5789 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5790 ok (result == TRUE, "Failed to undo typed characters.\n");
5791 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5792 result = strcmp(buffer, "acd");
5793 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5794 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5795 ok (result == TRUE, "Failed to undo typed characters.\n");
5796 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5797 result = strcmp(buffer, "abcd");
5798 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5799
5800 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5801 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5802 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5803 ok (result == TRUE, "Failed to clear the text.\n");
5804 simulate_typing_characters(hwnd, "one two three");
5805 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5806 ok (result == 0, "expected %d but got %d\n", 0, result);
5807 simulate_typing_characters(hwnd, " four five six");
5808 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5809 ok (result == TRUE, "Failed to undo typed characters.\n");
5810 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5811 result = strcmp(buffer, "one two three");
5812 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5813 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5814 ok (result == TRUE, "Failed to undo typed characters.\n");
5815 ok (result == TRUE, "Failed to undo typed characters.\n");
5816 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5817 result = strcmp(buffer, "");
5818 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5819
5820 DestroyWindow(hwnd);
5821 }
5822
5823 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5824 {
5825 int length;
5826
5827 /* MSDN lied, length is actually the number of bytes. */
5828 length = bytes / sizeof(WCHAR);
5829 switch(code)
5830 {
5831 case WB_ISDELIMITER:
5832 return text[pos] == 'X';
5833 case WB_LEFT:
5834 case WB_MOVEWORDLEFT:
5835 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5836 return pos-1;
5837 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5838 case WB_LEFTBREAK:
5839 pos--;
5840 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5841 pos--;
5842 return pos;
5843 case WB_RIGHT:
5844 case WB_MOVEWORDRIGHT:
5845 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5846 return pos+1;
5847 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5848 case WB_RIGHTBREAK:
5849 pos++;
5850 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5851 pos++;
5852 return pos;
5853 default:
5854 ok(FALSE, "Unexpected code %d\n", code);
5855 break;
5856 }
5857 return 0;
5858 }
5859
5860 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5861 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5862
5863 static void test_word_movement(void)
5864 {
5865 HWND hwnd;
5866 int result;
5867 int sel_start, sel_end;
5868 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5869
5870 /* multi-line control inserts CR normally */
5871 hwnd = new_richedit(NULL);
5872
5873 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5874 ok (result == TRUE, "Failed to clear the text.\n");
5875 SendMessage(hwnd, EM_SETSEL, 0, 0);
5876 /* |one two three */
5877
5878 SEND_CTRL_RIGHT(hwnd);
5879 /* one |two three */
5880 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5881 ok(sel_start == sel_end, "Selection should be empty\n");
5882 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5883
5884 SEND_CTRL_RIGHT(hwnd);
5885 /* one two |three */
5886 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5887 ok(sel_start == sel_end, "Selection should be empty\n");
5888 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5889
5890 SEND_CTRL_LEFT(hwnd);
5891 /* one |two three */
5892 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5893 ok(sel_start == sel_end, "Selection should be empty\n");
5894 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5895
5896 SEND_CTRL_LEFT(hwnd);
5897 /* |one two three */
5898 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5899 ok(sel_start == sel_end, "Selection should be empty\n");
5900 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5901
5902 SendMessage(hwnd, EM_SETSEL, 8, 8);
5903 /* one two | three */
5904 SEND_CTRL_RIGHT(hwnd);
5905 /* one two |three */
5906 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5907 ok(sel_start == sel_end, "Selection should be empty\n");
5908 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5909
5910 SendMessage(hwnd, EM_SETSEL, 11, 11);
5911 /* one two th|ree */
5912 SEND_CTRL_LEFT(hwnd);
5913 /* one two |three */
5914 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5915 ok(sel_start == sel_end, "Selection should be empty\n");
5916 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5917
5918 /* Test with a custom word break procedure that uses X as the delimiter. */
5919 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5920 ok (result == TRUE, "Failed to clear the text.\n");
5921 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5922 /* |one twoXthree */
5923 SEND_CTRL_RIGHT(hwnd);
5924 /* one twoX|three */
5925 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5926 ok(sel_start == sel_end, "Selection should be empty\n");
5927 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5928
5929 DestroyWindow(hwnd);
5930
5931 /* Make sure the behaviour is the same with a unicode richedit window,
5932 * and using unicode functions. */
5933 if (is_win9x)
5934 {
5935 skip("Cannot test with unicode richedit window\n");
5936 return;
5937 }
5938
5939 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5940 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5941 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5942
5943 /* Test with a custom word break procedure that uses X as the delimiter. */
5944 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5945 ok (result == TRUE, "Failed to clear the text.\n");
5946 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5947 /* |one twoXthree */
5948 SEND_CTRL_RIGHT(hwnd);
5949 /* one twoX|three */
5950 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5951 ok(sel_start == sel_end, "Selection should be empty\n");
5952 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5953
5954 DestroyWindow(hwnd);
5955 }
5956
5957 static void test_EM_CHARFROMPOS(void)
5958 {
5959 HWND hwnd;
5960 int result;
5961 RECT rcClient;
5962 POINTL point;
5963 point.x = 0;
5964 point.y = 40;
5965
5966 /* multi-line control inserts CR normally */
5967 hwnd = new_richedit(NULL);
5968 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5969 (LPARAM)"one two three four five six seven\reight");
5970
5971 GetClientRect(hwnd, &rcClient);
5972
5973 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5974 ok(result == 34, "expected character index of 34 but got %d\n", result);
5975
5976 /* Test with points outside the bounds of the richedit control. */
5977 point.x = -1;
5978 point.y = 40;
5979 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5980 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5981
5982 point.x = 1000;
5983 point.y = 0;
5984 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5985 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5986
5987 point.x = 1000;
5988 point.y = 40;
5989 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5990 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5991
5992 point.x = 1000;
5993 point.y = -1;
5994 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5995 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5996
5997 point.x = 1000;
5998 point.y = rcClient.bottom + 1;
5999 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6000 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6001
6002 point.x = 1000;
6003 point.y = rcClient.bottom;
6004 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6005 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6006
6007 DestroyWindow(hwnd);
6008 }
6009
6010 static void test_word_wrap(void)
6011 {
6012 HWND hwnd;
6013 POINTL point = {0, 60}; /* This point must be below the first line */
6014 const char *text = "Must be long enough to test line wrapping";
6015 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6016 int res, pos, lines;
6017
6018 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6019 * when specified on window creation and set later. */
6020 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6021 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6022 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6023 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6024 ok(res, "WM_SETTEXT failed.\n");
6025 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6026 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6027 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6028 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6029
6030 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6031 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6032 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6033 DestroyWindow(hwnd);
6034
6035 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6036 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6037 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6038
6039 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6040 ok(res, "WM_SETTEXT failed.\n");
6041 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6042 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6043 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6044 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6045
6046 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6047 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6048 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6049 DestroyWindow(hwnd);
6050
6051 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6052 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6053 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6054 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6055 ok(res, "WM_SETTEXT failed.\n");
6056 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6057 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6058
6059 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6060 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6061 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6062 DestroyWindow(hwnd);
6063
6064 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6065 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6066 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6067 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6068 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6069 ok(res, "WM_SETTEXT failed.\n");
6070 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6071 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6072
6073 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6074 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6075 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6076
6077 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6078 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6079 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6080 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6081 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6082
6083 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6084 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6085 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6086 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6087 DestroyWindow(hwnd);
6088
6089 /* Test to see if wrapping happens with redraw disabled. */
6090 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6091 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6092 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6093 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6094 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6095 ok(res, "EM_REPLACESEL failed.\n");
6096 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6097 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6098 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6099 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6100 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6101
6102 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6103 DestroyWindow(hwnd);
6104 }
6105
6106 static void test_autoscroll(void)
6107 {
6108 HWND hwnd = new_richedit(NULL);
6109 int lines, ret, redraw;
6110 POINT pt;
6111
6112 for (redraw = 0; redraw <= 1; redraw++) {
6113 trace("testing with WM_SETREDRAW=%d\n", redraw);
6114 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6115 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6116 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6117 ok(lines == 8, "%d lines instead of 8\n", lines);
6118 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6119 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6120 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6121 ret = GetWindowLong(hwnd, GWL_STYLE);
6122 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6123
6124 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6125 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6126 ok(lines == 1, "%d lines instead of 1\n", lines);
6127 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6128 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6129 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6130 ret = GetWindowLong(hwnd, GWL_STYLE);
6131 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6132 }
6133
6134 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6135 DestroyWindow(hwnd);
6136
6137 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6138 * auto vertical/horizontal scrolling options. */
6139 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6140 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6141 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6142 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6143 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6144 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6145 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6146 ret = GetWindowLong(hwnd, GWL_STYLE);
6147 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6148 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6149 DestroyWindow(hwnd);
6150
6151 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6152 WS_POPUP|ES_MULTILINE,
6153 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6154 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6155 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6156 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6157 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6158 ret = GetWindowLong(hwnd, GWL_STYLE);
6159 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6160 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6161 DestroyWindow(hwnd);
6162 }
6163
6164
6165 static void test_format_rect(void)
6166 {
6167 HWND hwnd;
6168 RECT rc, expected, clientRect;
6169 int n;
6170 DWORD options;
6171
6172 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6173 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6174 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6175 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6176
6177 GetClientRect(hwnd, &clientRect);
6178
6179 expected = clientRect;
6180 expected.left += 1;
6181 expected.right -= 1;
6182 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6183 ok(rc.top == expected.top && rc.left == expected.left &&
6184 rc.bottom == expected.bottom && rc.right == expected.right,
6185 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6186 rc.top, rc.left, rc.bottom, rc.right,
6187 expected.top, expected.left, expected.bottom, expected.right);
6188
6189 for (n = -3; n <= 3; n++)
6190 {
6191 rc = clientRect;
6192 rc.top += n;
6193 rc.left += n;
6194 rc.bottom -= n;
6195 rc.right -= n;
6196 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6197
6198 expected = rc;
6199 expected.top = max(0, rc.top);
6200 expected.left = max(0, rc.left);
6201 expected.bottom = min(clientRect.bottom, rc.bottom);
6202 expected.right = min(clientRect.right, rc.right);
6203 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6204 ok(rc.top == expected.top && rc.left == expected.left &&
6205 rc.bottom == expected.bottom && rc.right == expected.right,
6206 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6207 n, rc.top, rc.left, rc.bottom, rc.right,
6208 expected.top, expected.left, expected.bottom, expected.right);
6209 }
6210
6211 rc = clientRect;
6212 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6213 expected = clientRect;
6214 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6215 ok(rc.top == expected.top && rc.left == expected.left &&
6216 rc.bottom == expected.bottom && rc.right == expected.right,
6217 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6218 rc.top, rc.left, rc.bottom, rc.right,
6219 expected.top, expected.left, expected.bottom, expected.right);
6220
6221 /* Adding the selectionbar adds the selectionbar width to the left side. */
6222 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6223 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6224 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6225 expected.left += 8; /* selection bar width */
6226 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6227 ok(rc.top == expected.top && rc.left == expected.left &&
6228 rc.bottom == expected.bottom && rc.right == expected.right,
6229 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6230 rc.top, rc.left, rc.bottom, rc.right,
6231 expected.top, expected.left, expected.bottom, expected.right);
6232
6233 rc = clientRect;
6234 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6235 expected = clientRect;
6236 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6237 ok(rc.top == expected.top && rc.left == expected.left &&
6238 rc.bottom == expected.bottom && rc.right == expected.right,
6239 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6240 rc.top, rc.left, rc.bottom, rc.right,
6241 expected.top, expected.left, expected.bottom, expected.right);
6242
6243 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6244 * even if the left side is already 0. */
6245 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6246 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6247 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6248 expected.left -= 8; /* selection bar width */
6249 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6250 ok(rc.top == expected.top && rc.left == expected.left &&
6251 rc.bottom == expected.bottom && rc.right == expected.right,
6252 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6253 rc.top, rc.left, rc.bottom, rc.right,
6254 expected.top, expected.left, expected.bottom, expected.right);
6255
6256 /* Set the absolute value of the formatting rectangle. */
6257 rc = clientRect;
6258 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6259 expected = clientRect;
6260 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6261 ok(rc.top == expected.top && rc.left == expected.left &&
6262 rc.bottom == expected.bottom && rc.right == expected.right,
6263 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6264 n, rc.top, rc.left, rc.bottom, rc.right,
6265 expected.top, expected.left, expected.bottom, expected.right);
6266
6267 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6268 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6269 * tests show that this isn't true. */
6270 rc.top = 15;
6271 rc.left = 15;
6272 rc.bottom = clientRect.bottom - 15;
6273 rc.right = clientRect.right - 15;
6274 expected = rc;
6275 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6276 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6277 ok(rc.top == expected.top && rc.left == expected.left &&
6278 rc.bottom == expected.bottom && rc.right == expected.right,
6279 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6280 rc.top, rc.left, rc.bottom, rc.right,
6281 expected.top, expected.left, expected.bottom, expected.right);
6282
6283 /* For some reason it does not limit the values to the client rect with
6284 * a WPARAM value of 1. */
6285 rc.top = -15;
6286 rc.left = -15;
6287 rc.bottom = clientRect.bottom + 15;
6288 rc.right = clientRect.right + 15;
6289 expected = rc;
6290 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6291 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6292 ok(rc.top == expected.top && rc.left == expected.left &&
6293 rc.bottom == expected.bottom && rc.right == expected.right,
6294 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6295 rc.top, rc.left, rc.bottom, rc.right,
6296 expected.top, expected.left, expected.bottom, expected.right);
6297
6298 DestroyWindow(hwnd);
6299
6300 /* The extended window style affects the formatting rectangle. */
6301 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6302 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6303 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6304 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6305
6306 GetClientRect(hwnd, &clientRect);
6307
6308 expected = clientRect;
6309 expected.left += 1;
6310 expected.top += 1;
6311 expected.right -= 1;
6312 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6313 ok(rc.top == expected.top && rc.left == expected.left &&
6314 rc.bottom == expected.bottom && rc.right == expected.right,
6315 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6316 rc.top, rc.left, rc.bottom, rc.right,
6317 expected.top, expected.left, expected.bottom, expected.right);
6318
6319 rc = clientRect;
6320 rc.top += 5;
6321 rc.left += 5;
6322 rc.bottom -= 5;
6323 rc.right -= 5;
6324 expected = rc;
6325 expected.top -= 1;
6326 expected.left -= 1;
6327 expected.right += 1;
6328 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6329 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6330 ok(rc.top == expected.top && rc.left == expected.left &&
6331 rc.bottom == expected.bottom && rc.right == expected.right,
6332 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6333 rc.top, rc.left, rc.bottom, rc.right,
6334 expected.top, expected.left, expected.bottom, expected.right);
6335
6336 DestroyWindow(hwnd);
6337 }
6338
6339 static void test_WM_GETDLGCODE(void)
6340 {
6341 HWND hwnd;
6342 UINT res, expected;
6343 MSG msg;
6344
6345 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6346
6347 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6348 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6349 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6350 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6351 msg.hwnd = hwnd;
6352 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6353 expected = expected | DLGC_WANTMESSAGE;
6354 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6355 res, expected);
6356 DestroyWindow(hwnd);
6357
6358 msg.message = WM_KEYDOWN;
6359 msg.wParam = VK_RETURN;
6360 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6361 msg.pt.x = 0;
6362 msg.pt.y = 0;
6363 msg.time = GetTickCount();
6364
6365 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6366 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6367 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6368 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6369 msg.hwnd = hwnd;
6370 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6371 expected = expected | DLGC_WANTMESSAGE;
6372 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6373 res, expected);
6374 DestroyWindow(hwnd);
6375
6376 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6377 ES_MULTILINE|WS_POPUP,
6378 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6379 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6380 msg.hwnd = hwnd;
6381 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6382 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6383 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6384 res, expected);
6385 DestroyWindow(hwnd);
6386
6387 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6388 ES_WANTRETURN|WS_POPUP,
6389 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6390 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6391 msg.hwnd = hwnd;
6392 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6393 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6394 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6395 res, expected);
6396 DestroyWindow(hwnd);
6397
6398 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6399 WS_POPUP,
6400 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6401 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6402 msg.hwnd = hwnd;
6403 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6404 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6405 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6406 res, expected);
6407 DestroyWindow(hwnd);
6408
6409 msg.wParam = VK_TAB;
6410 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6411
6412 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6413 ES_MULTILINE|WS_POPUP,
6414 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6415 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6416 msg.hwnd = hwnd;
6417 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6418 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6419 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6420 res, expected);
6421 DestroyWindow(hwnd);
6422
6423 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6424 WS_POPUP,
6425 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6426 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6427 msg.hwnd = hwnd;
6428 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6429 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6430 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6431 res, expected);
6432 DestroyWindow(hwnd);
6433
6434 hold_key(VK_CONTROL);
6435
6436 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6437 ES_MULTILINE|WS_POPUP,
6438 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6439 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6440 msg.hwnd = hwnd;
6441 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6442 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6443 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6444 res, expected);
6445 DestroyWindow(hwnd);
6446
6447 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6448 WS_POPUP,
6449 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6450 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6451 msg.hwnd = hwnd;
6452 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6453 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6454 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6455 res, expected);
6456 DestroyWindow(hwnd);
6457
6458 release_key(VK_CONTROL);
6459
6460 msg.wParam = 'a';
6461 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6462
6463 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6464 ES_MULTILINE|WS_POPUP,
6465 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6466 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6467 msg.hwnd = hwnd;
6468 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6469 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6470 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6471 res, expected);
6472 DestroyWindow(hwnd);
6473
6474 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6475 WS_POPUP,
6476 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6477 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6478 msg.hwnd = hwnd;
6479 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6480 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6481 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6482 res, expected);
6483 DestroyWindow(hwnd);
6484
6485 msg.message = WM_CHAR;
6486
6487 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6488 ES_MULTILINE|WS_POPUP,
6489 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6490 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6491 msg.hwnd = hwnd;
6492 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6493 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6494 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6495 res, expected);
6496 DestroyWindow(hwnd);
6497
6498 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6499 WS_POPUP,
6500 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6501 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6502 msg.hwnd = hwnd;
6503 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6504 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6505 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6506 res, expected);
6507 DestroyWindow(hwnd);
6508 }
6509
6510 static void test_zoom(void)
6511 {
6512 HWND hwnd;
6513 UINT ret;
6514 RECT rc;
6515 POINT pt;
6516 int numerator, denominator;
6517
6518 hwnd = new_richedit(NULL);
6519 GetClientRect(hwnd, &rc);
6520 pt.x = (rc.right - rc.left) / 2;
6521 pt.y = (rc.bottom - rc.top) / 2;
6522 ClientToScreen(hwnd, &pt);
6523
6524 /* Test initial zoom value */
6525 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6526 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6527 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6528 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6529
6530 /* test scroll wheel */
6531 hold_key(VK_CONTROL);
6532 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6533 MAKELPARAM(pt.x, pt.y));
6534 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6535 release_key(VK_CONTROL);
6536
6537 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6538 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6539 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6540 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6541
6542 /* Test how much the mouse wheel can zoom in and out. */
6543 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6544 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6545
6546 hold_key(VK_CONTROL);
6547 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6548 MAKELPARAM(pt.x, pt.y));
6549 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6550 release_key(VK_CONTROL);
6551
6552 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6553 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6554 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6555 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6556
6557 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6558 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6559
6560 hold_key(VK_CONTROL);
6561 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6562 MAKELPARAM(pt.x, pt.y));
6563 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6564 release_key(VK_CONTROL);
6565
6566 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6567 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6568 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6569 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6570
6571 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6572 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6573
6574 hold_key(VK_CONTROL);
6575 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6576 MAKELPARAM(pt.x, pt.y));
6577 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6578 release_key(VK_CONTROL);
6579
6580 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6581 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6582 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6583 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6584
6585 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6586 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6587
6588 hold_key(VK_CONTROL);
6589 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6590 MAKELPARAM(pt.x, pt.y));
6591 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6592 release_key(VK_CONTROL);
6593
6594 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6595 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6596 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6597 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6598
6599 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6600 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6601 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6602
6603 hold_key(VK_CONTROL);
6604 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6605 MAKELPARAM(pt.x, pt.y));
6606 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6607 release_key(VK_CONTROL);
6608
6609 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6610 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6611 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6612 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6613
6614 /* Test bounds checking on EM_SETZOOM */
6615 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6616 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6617
6618 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6619 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6620
6621 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6622 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6623
6624 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6625 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6626 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6627 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6628
6629 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6630 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6631
6632 /* See if negative numbers are accepted. */
6633 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6634 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6635
6636 /* See if negative numbers are accepted. */
6637 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6638 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6639
6640 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6641 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6642 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6643 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6644
6645 /* Reset the zoom value */
6646 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6647 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6648
6649 DestroyWindow(hwnd);
6650 }
6651
6652 START_TEST( editor )
6653 {
6654 /* Must explicitly LoadLibrary(). The test has no references to functions in
6655 * RICHED20.DLL, so the linker doesn't actually link to it. */
6656 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6657 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6658
6659 is_win9x = GetVersion() & 0x80000000;
6660
6661 test_WM_CHAR();
6662 test_EM_FINDTEXT();
6663 test_EM_GETLINE();
6664 test_EM_POSFROMCHAR();
6665 test_EM_SCROLLCARET();
6666 test_EM_SCROLL();
6667 test_scrollbar_visibility();
6668 test_WM_SETTEXT();
6669 test_EM_LINELENGTH();
6670 test_EM_SETCHARFORMAT();
6671 test_EM_SETTEXTMODE();
6672 test_TM_PLAINTEXT();
6673 test_EM_SETOPTIONS();
6674 test_WM_GETTEXT();
6675 test_EM_GETTEXTRANGE();
6676 test_EM_GETSELTEXT();
6677 test_EM_SETUNDOLIMIT();
6678 test_ES_PASSWORD();
6679 test_EM_SETTEXTEX();
6680 test_EM_LIMITTEXT();
6681 test_EM_EXLIMITTEXT();
6682 test_EM_GETLIMITTEXT();
6683 test_WM_SETFONT();
6684 test_EM_GETMODIFY();
6685 test_EM_EXSETSEL();
6686 test_WM_PASTE();
6687 test_EM_STREAMIN();
6688 test_EM_STREAMOUT();
6689 test_EM_STREAMOUT_FONTTBL();
6690 test_EM_StreamIn_Undo();
6691 test_EM_FORMATRANGE();
6692 test_unicode_conversions();
6693 test_EM_GETTEXTLENGTHEX();
6694 test_EM_REPLACESEL(1);
6695 test_EM_REPLACESEL(0);
6696 test_WM_NOTIFY();
6697 test_EM_AUTOURLDETECT();
6698 test_eventMask();
6699 test_undo_coalescing();
6700 test_word_movement();
6701 test_EM_CHARFROMPOS();
6702 test_SETPARAFORMAT();
6703 test_word_wrap();
6704 test_autoscroll();
6705 test_format_rect();
6706 test_WM_GETDLGCODE();
6707 test_zoom();
6708
6709 /* Set the environment variable WINETEST_RICHED20 to keep windows
6710 * responsive and open for 30 seconds. This is useful for debugging.
6711 */
6712 if (getenv( "WINETEST_RICHED20" )) {
6713 keep_responsive(30);
6714 }
6715
6716 OleFlushClipboard();
6717 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
6718 }