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