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