[STORAHCI] Merge Storport Miniport driver by Aman Priyadarshi in GSoC.
[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 <commdlg.h>
34 #include <time.h>
35 #include <wine/test.h>
36
37 #define ID_RICHEDITTESTDBUTTON 0x123
38
39 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40
41 #define ok_w3(format, szString1, szString2, szString3) \
42 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
44 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
45 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
46 format, string1, string2, string3);
47
48 static HMODULE hmoduleRichEdit;
49 static BOOL is_lang_japanese;
50
51 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
52 HWND hwnd;
53 hwnd = CreateWindowA(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_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
61 HWND hwnd;
62 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
63 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
64 hmoduleRichEdit, NULL);
65 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
66 return hwnd;
67 }
68
69 static HWND new_richedit(HWND parent) {
70 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
71 }
72
73 static HWND new_richeditW(HWND parent) {
74 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 }
76
77 /* Keeps the window reponsive for the deley_time in seconds.
78 * This is useful for debugging a test to see what is happening. */
79 static void keep_responsive(time_t delay_time)
80 {
81 MSG msg;
82 time_t end;
83
84 /* The message pump uses PeekMessage() to empty the queue and then
85 * sleeps for 50ms before retrying the queue. */
86 end = time(NULL) + delay_time;
87 while (time(NULL) < end) {
88 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
89 TranslateMessage(&msg);
90 DispatchMessageA(&msg);
91 } else {
92 Sleep(50);
93 }
94 }
95 }
96
97 static void simulate_typing_characters(HWND hwnd, const char* szChars)
98 {
99 int ret;
100
101 while (*szChars != '\0') {
102 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
103 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
104 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
105 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
106 szChars++;
107 }
108 }
109
110 static BOOL hold_key(int vk)
111 {
112 BYTE key_state[256];
113 BOOL result;
114
115 result = GetKeyboardState(key_state);
116 ok(result, "GetKeyboardState failed.\n");
117 if (!result) return FALSE;
118 key_state[vk] |= 0x80;
119 result = SetKeyboardState(key_state);
120 ok(result, "SetKeyboardState failed.\n");
121 return result != 0;
122 }
123
124 static BOOL release_key(int vk)
125 {
126 BYTE key_state[256];
127 BOOL result;
128
129 result = GetKeyboardState(key_state);
130 ok(result, "GetKeyboardState failed.\n");
131 if (!result) return FALSE;
132 key_state[vk] &= ~0x80;
133 result = SetKeyboardState(key_state);
134 ok(result, "SetKeyboardState failed.\n");
135 return result != 0;
136 }
137
138 static const char haystack[] = "WINEWine wineWine wine WineWine";
139 /* ^0 ^10 ^20 ^30 */
140
141 struct find_s {
142 int start;
143 int end;
144 const char *needle;
145 int flags;
146 int expected_loc;
147 };
148
149
150 static struct find_s find_tests[] = {
151 /* Find in empty text */
152 {0, -1, "foo", FR_DOWN, -1},
153 {0, -1, "foo", 0, -1},
154 {0, -1, "", FR_DOWN, -1},
155 {20, 5, "foo", FR_DOWN, -1},
156 {5, 20, "foo", FR_DOWN, -1}
157 };
158
159 static struct find_s find_tests2[] = {
160 /* No-result find */
161 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
162 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
163
164 /* Subsequent finds */
165 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
166 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
167 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
168 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
169
170 /* Find backwards */
171 {19, 20, "Wine", FR_MATCHCASE, 13},
172 {10, 20, "Wine", FR_MATCHCASE, 4},
173 {20, 10, "Wine", FR_MATCHCASE, 13},
174
175 /* Case-insensitive */
176 {1, 31, "wInE", FR_DOWN, 4},
177 {1, 31, "Wine", FR_DOWN, 4},
178
179 /* High-to-low ranges */
180 {20, 5, "Wine", FR_DOWN, -1},
181 {2, 1, "Wine", FR_DOWN, -1},
182 {30, 29, "Wine", FR_DOWN, -1},
183 {20, 5, "Wine", 0, 13},
184
185 /* Find nothing */
186 {5, 10, "", FR_DOWN, -1},
187 {10, 5, "", FR_DOWN, -1},
188 {0, -1, "", FR_DOWN, -1},
189 {10, 5, "", 0, -1},
190
191 /* Whole-word search */
192 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
194 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
195 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
196 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
197 {11, -1, "winewine", FR_WHOLEWORD, 0},
198 {31, -1, "winewine", FR_WHOLEWORD, 23},
199
200 /* Bad ranges */
201 {5, 200, "XXX", FR_DOWN, -1},
202 {-20, 20, "Wine", FR_DOWN, -1},
203 {-20, 20, "Wine", FR_DOWN, -1},
204 {-15, -20, "Wine", FR_DOWN, -1},
205 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
206
207 /* Check the case noted in bug 4479 where matches at end aren't recognized */
208 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
209 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
210 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
211 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
213
214 /* The backwards case of bug 4479; bounds look right
215 * Fails because backward find is wrong */
216 {19, 20, "WINE", FR_MATCHCASE, 0},
217 {0, 20, "WINE", FR_MATCHCASE, -1},
218
219 {0, -1, "wineWine wine", 0, -1},
220 };
221
222 static WCHAR *atowstr(const char *str)
223 {
224 WCHAR *ret;
225 DWORD len;
226 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
227 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
228 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
229 return ret;
230 }
231
232 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
233 {
234 int findloc;
235
236 if(unicode){
237 FINDTEXTW ftw;
238 memset(&ftw, 0, sizeof(ftw));
239 ftw.chrg.cpMin = f->start;
240 ftw.chrg.cpMax = f->end;
241 ftw.lpstrText = atowstr(f->needle);
242
243 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
244 ok(findloc == f->expected_loc,
245 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
246 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
247
248 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
249 ok(findloc == f->expected_loc,
250 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
251 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
252
253 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
254 }else{
255 FINDTEXTA fta;
256 memset(&fta, 0, sizeof(fta));
257 fta.chrg.cpMin = f->start;
258 fta.chrg.cpMax = f->end;
259 fta.lpstrText = f->needle;
260
261 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
262 ok(findloc == f->expected_loc,
263 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
264 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
265 }
266 }
267
268 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
269 int id, BOOL unicode)
270 {
271 int findloc;
272 int expected_end_loc;
273
274 if(unicode){
275 FINDTEXTEXW ftw;
276 memset(&ftw, 0, sizeof(ftw));
277 ftw.chrg.cpMin = f->start;
278 ftw.chrg.cpMax = f->end;
279 ftw.lpstrText = atowstr(f->needle);
280 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
281 ok(findloc == f->expected_loc,
282 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
283 name, id, f->needle, f->start, f->end, f->flags, findloc);
284 ok(ftw.chrgText.cpMin == f->expected_loc,
285 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
286 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
287 expected_end_loc = ((f->expected_loc == -1) ? -1
288 : f->expected_loc + strlen(f->needle));
289 ok(ftw.chrgText.cpMax == expected_end_loc,
290 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
291 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
292 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
293 }else{
294 FINDTEXTEXA fta;
295 memset(&fta, 0, sizeof(fta));
296 fta.chrg.cpMin = f->start;
297 fta.chrg.cpMax = f->end;
298 fta.lpstrText = f->needle;
299 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
300 ok(findloc == f->expected_loc,
301 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
302 name, id, f->needle, f->start, f->end, f->flags, findloc);
303 ok(fta.chrgText.cpMin == f->expected_loc,
304 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
305 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
306 expected_end_loc = ((f->expected_loc == -1) ? -1
307 : f->expected_loc + strlen(f->needle));
308 ok(fta.chrgText.cpMax == expected_end_loc,
309 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
310 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
311 }
312 }
313
314 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
315 int num_tests, BOOL unicode)
316 {
317 int i;
318
319 for (i = 0; i < num_tests; i++) {
320 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
321 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
322 }
323 }
324
325 static void test_EM_FINDTEXT(BOOL unicode)
326 {
327 HWND hwndRichEdit;
328 CHARFORMAT2A cf2;
329
330 if(unicode)
331 hwndRichEdit = new_richeditW(NULL);
332 else
333 hwndRichEdit = new_richedit(NULL);
334
335 /* Empty rich edit control */
336 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
337 sizeof(find_tests)/sizeof(struct find_s), unicode);
338
339 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
340
341 /* Haystack text */
342 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
343 sizeof(find_tests2)/sizeof(struct find_s), unicode);
344
345 /* Setting a format on an arbitrary range should have no effect in search
346 results. This tests correct offset reporting across runs. */
347 cf2.cbSize = sizeof(CHARFORMAT2A);
348 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
349 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
350 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
351 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
352 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
353
354 /* Haystack text, again */
355 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
356 sizeof(find_tests2)/sizeof(struct find_s), unicode);
357
358 /* Yet another range */
359 cf2.dwMask = CFM_BOLD | cf2.dwMask;
360 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
361 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
362 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
363
364 /* Haystack text, again */
365 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
366 sizeof(find_tests2)/sizeof(struct find_s), unicode);
367
368 DestroyWindow(hwndRichEdit);
369 }
370
371 static const struct getline_s {
372 int line;
373 size_t buffer_len;
374 const char *text;
375 } gl[] = {
376 {0, 10, "foo bar\r"},
377 {1, 10, "\r"},
378 {2, 10, "bar\r"},
379 {3, 10, "\r"},
380
381 /* Buffer smaller than line length */
382 {0, 2, "foo bar\r"},
383 {0, 1, "foo bar\r"},
384 {0, 0, "foo bar\r"}
385 };
386
387 static void test_EM_GETLINE(void)
388 {
389 int i;
390 HWND hwndRichEdit = new_richedit(NULL);
391 static const int nBuf = 1024;
392 char dest[1024], origdest[1024];
393 const char text[] = "foo bar\n"
394 "\n"
395 "bar\n";
396
397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
398
399 memset(origdest, 0xBB, nBuf);
400 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
401 {
402 int nCopied;
403 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
404 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
405 memset(dest, 0xBB, nBuf);
406 *(WORD *) dest = gl[i].buffer_len;
407
408 /* EM_GETLINE appends a "\r\0" to the end of the line
409 * nCopied counts up to and including the '\r' */
410 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
411 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
412 expected_nCopied);
413 /* two special cases since a parameter is passed via dest */
414 if (gl[i].buffer_len == 0)
415 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
416 "buffer_len=0\n");
417 else if (gl[i].buffer_len == 1)
418 ok(dest[0] == gl[i].text[0] && !dest[1] &&
419 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
420 else
421 {
422 /* Prepare hex strings of buffers to dump on failure. */
423 char expectedbuf[1024];
424 char resultbuf[1024];
425 int j;
426 resultbuf[0] = '\0';
427 for (j = 0; j < 32; j++)
428 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
429 expectedbuf[0] = '\0';
430 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
431 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
432 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
433 sprintf(expectedbuf+strlen(expectedbuf), "??");
434 for (; j < 32; j++) /* Bytes after declared buffer size */
435 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
436
437 /* Test the part of the buffer that is expected to be written according
438 * to the MSDN documentation fo EM_GETLINE, which does not state that
439 * a NULL terminating character will be added unless no text is copied.
440 *
441 * Windows NT does not append a NULL terminating character, but
442 * Windows 2000 and up do append a NULL terminating character if there
443 * is space in the buffer. The test will ignore this difference. */
444 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
445 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
446 i, expected_bytes_written, expectedbuf, resultbuf);
447 /* Test the part of the buffer after the declared length to make sure
448 * there are no buffer overruns. */
449 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
450 nBuf - gl[i].buffer_len),
451 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
452 i, expected_bytes_written, expectedbuf, resultbuf);
453 }
454 }
455
456 DestroyWindow(hwndRichEdit);
457 }
458
459 static void test_EM_LINELENGTH(void)
460 {
461 HWND hwndRichEdit = new_richedit(NULL);
462 const char * text =
463 "richedit1\r"
464 "richedit1\n"
465 "richedit1\r\n"
466 "richedit1";
467 int offset_test[10][2] = {
468 {0, 9},
469 {5, 9},
470 {10, 9},
471 {15, 9},
472 {20, 9},
473 {25, 9},
474 {30, 9},
475 {35, 9},
476 {40, 0},
477 {45, 0},
478 };
479 int i;
480 LRESULT result;
481
482 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
483
484 for (i = 0; i < 10; i++) {
485 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
486 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
487 offset_test[i][0], result, offset_test[i][1]);
488 }
489
490 /* Test with multibyte character */
491 if (!is_lang_japanese)
492 skip("Skip multibyte character tests on non-Japanese platform\n");
493 else
494 {
495 const char *text1 =
496 "wine\n"
497 "richedit\x8e\xf0\n"
498 "wine";
499 int offset_test1[3][2] = {
500 {0, 4}, /* Line 1: |wine\n */
501 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
502 {15, 4}, /* Line 3: |wine */
503 };
504 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
505 for (i = 0; i < sizeof(offset_test1)/sizeof(offset_test1[0]); i++) {
506 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
507 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
508 offset_test1[i][0], result, offset_test1[i][1]);
509 }
510 }
511
512 DestroyWindow(hwndRichEdit);
513 }
514
515 static int get_scroll_pos_y(HWND hwnd)
516 {
517 POINT p = {-1, -1};
518 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
519 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
520 return p.y;
521 }
522
523 static void move_cursor(HWND hwnd, LONG charindex)
524 {
525 CHARRANGE cr;
526 cr.cpMax = charindex;
527 cr.cpMin = charindex;
528 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
529 }
530
531 static void line_scroll(HWND hwnd, int amount)
532 {
533 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
534 }
535
536 static void test_EM_SCROLLCARET(void)
537 {
538 int prevY, curY;
539 const char text[] = "aa\n"
540 "this is a long line of text that should be longer than the "
541 "control's width\n"
542 "cc\n"
543 "dd\n"
544 "ee\n"
545 "ff\n"
546 "gg\n"
547 "hh\n";
548 /* The richedit window height needs to be large enough vertically to fit in
549 * more than two lines of text, so the new_richedit function can't be used
550 * since a height of 60 was not large enough on some systems.
551 */
552 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
553 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
554 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
555 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
556
557 /* Can't verify this */
558 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
559
560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
561
562 /* Caret above visible window */
563 line_scroll(hwndRichEdit, 3);
564 prevY = get_scroll_pos_y(hwndRichEdit);
565 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
566 curY = get_scroll_pos_y(hwndRichEdit);
567 ok(prevY != curY, "%d == %d\n", prevY, curY);
568
569 /* Caret below visible window */
570 move_cursor(hwndRichEdit, sizeof(text) - 1);
571 line_scroll(hwndRichEdit, -3);
572 prevY = get_scroll_pos_y(hwndRichEdit);
573 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
574 curY = get_scroll_pos_y(hwndRichEdit);
575 ok(prevY != curY, "%d == %d\n", prevY, curY);
576
577 /* Caret in visible window */
578 move_cursor(hwndRichEdit, sizeof(text) - 2);
579 prevY = get_scroll_pos_y(hwndRichEdit);
580 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
581 curY = get_scroll_pos_y(hwndRichEdit);
582 ok(prevY == curY, "%d != %d\n", prevY, curY);
583
584 /* Caret still in visible window */
585 line_scroll(hwndRichEdit, -1);
586 prevY = get_scroll_pos_y(hwndRichEdit);
587 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
588 curY = get_scroll_pos_y(hwndRichEdit);
589 ok(prevY == curY, "%d != %d\n", prevY, curY);
590
591 DestroyWindow(hwndRichEdit);
592 }
593
594 static void test_EM_POSFROMCHAR(void)
595 {
596 HWND hwndRichEdit = new_richedit(NULL);
597 int i, expected;
598 LRESULT result;
599 unsigned int height = 0;
600 int xpos = 0;
601 POINTL pt;
602 LOCALESIGNATURE sig;
603 BOOL rtl;
604 PARAFORMAT2 fmt;
605 static const char text[] = "aa\n"
606 "this is a long line of text that should be longer than the "
607 "control's width\n"
608 "cc\n"
609 "dd\n"
610 "ee\n"
611 "ff\n"
612 "gg\n"
613 "hh\n";
614
615 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
616 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
617 (sig.lsUsb[3] & 0x08000000) != 0);
618
619 /* Fill the control to lines to ensure that most of them are offscreen */
620 for (i = 0; i < 50; i++)
621 {
622 /* Do not modify the string; it is exactly 16 characters long. */
623 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
624 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
625 }
626
627 /*
628 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
629 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
630 Richedit 3.0 accepts either of the above API conventions.
631 */
632
633 /* Testing Richedit 2.0 API format */
634
635 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
636 Since all lines are identical and drawn with the same font,
637 they should have the same height... right?
638 */
639 for (i = 0; i < 50; i++)
640 {
641 /* All the lines are 16 characters long */
642 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
643 if (i == 0)
644 {
645 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
646 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
647 xpos = LOWORD(result);
648 }
649 else if (i == 1)
650 {
651 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
652 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
653 height = HIWORD(result);
654 }
655 else
656 {
657 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
658 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
659 }
660 }
661
662 /* Testing position at end of text */
663 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
664 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
665 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
666
667 /* Testing position way past end of text */
668 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
669 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
670 expected = (rtl ? 8 : 1);
671 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
672
673 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
674 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
675 for (i = 0; i < 50; i++)
676 {
677 /* All the lines are 16 characters long */
678 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
679 ok((signed short)(HIWORD(result)) == (i - 1) * height,
680 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
681 (signed short)(HIWORD(result)), (i - 1) * height);
682 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
683 }
684
685 /* Testing position at end of text */
686 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
687 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
688 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
689
690 /* Testing position way past end of text */
691 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
692 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
693 expected = (rtl ? 8 : 1);
694 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
695
696 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
697 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
698 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
699
700 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
701 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
702 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
703 xpos = LOWORD(result);
704
705 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
706 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
707 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
708 ok((signed short)(LOWORD(result)) < xpos,
709 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
710 (signed short)(LOWORD(result)), xpos);
711 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
712
713 /* Test around end of text that doesn't end in a newline. */
714 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
715 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
716 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
717 ok(pt.x > 1, "pt.x = %d\n", pt.x);
718 xpos = pt.x;
719 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
721 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
722 xpos = (rtl ? pt.x + 7 : pt.x);
723 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
725 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
726
727 /* Try a negative position. */
728 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
729 ok(pt.x == 1, "pt.x = %d\n", pt.x);
730
731 /* test negative indentation */
732 SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
733 (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
734 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
735 ok(pt.x == 1, "pt.x = %d\n", pt.x);
736
737 fmt.cbSize = sizeof(fmt);
738 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
739 ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
740 ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
741 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
742
743 DestroyWindow(hwndRichEdit);
744 }
745
746 static void test_EM_SETCHARFORMAT(void)
747 {
748 HWND hwndRichEdit = new_richedit(NULL);
749 CHARFORMAT2A cf2;
750 CHARFORMAT2W cfW;
751 int rc = 0;
752 int tested_effects[] = {
753 CFE_BOLD,
754 CFE_ITALIC,
755 CFE_UNDERLINE,
756 CFE_STRIKEOUT,
757 CFE_PROTECTED,
758 CFE_LINK,
759 CFE_SUBSCRIPT,
760 CFE_SUPERSCRIPT,
761 0
762 };
763 int i;
764 CHARRANGE cr;
765 LOCALESIGNATURE sig;
766 BOOL rtl;
767 DWORD expect_effects;
768
769 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
770 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
771 (sig.lsUsb[3] & 0x08000000) != 0);
772
773 /* check charformat defaults */
774 memset(&cf2, 0, sizeof(CHARFORMAT2A));
775 cf2.cbSize = sizeof(CHARFORMAT2A);
776 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
777 ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
778 expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
779 if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
780 ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
781 ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
782 ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
783 ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
784 ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
785 ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
786 ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
787 ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
788
789 /* Invalid flags, CHARFORMAT2 structure blanked out */
790 memset(&cf2, 0, sizeof(cf2));
791 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
792 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
793
794 /* A valid flag, CHARFORMAT2 structure blanked out */
795 memset(&cf2, 0, sizeof(cf2));
796 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
797 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
798
799 /* A valid flag, CHARFORMAT2 structure blanked out */
800 memset(&cf2, 0, sizeof(cf2));
801 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
802 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
803
804 /* A valid flag, CHARFORMAT2 structure blanked out */
805 memset(&cf2, 0, sizeof(cf2));
806 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
807 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
808
809 /* A valid flag, CHARFORMAT2 structure blanked out */
810 memset(&cf2, 0, sizeof(cf2));
811 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
812 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
813
814 /* Invalid flags, CHARFORMAT2 structure minimally filled */
815 memset(&cf2, 0, sizeof(cf2));
816 cf2.cbSize = sizeof(CHARFORMAT2A);
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
818 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
819 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
820 ok(rc == FALSE, "Should not be able to undo here.\n");
821 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
822
823 /* A valid flag, CHARFORMAT2 structure minimally filled */
824 memset(&cf2, 0, sizeof(cf2));
825 cf2.cbSize = sizeof(CHARFORMAT2A);
826 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
827 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
828 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
829 ok(rc == FALSE, "Should not be able to undo here.\n");
830 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
831
832 /* A valid flag, CHARFORMAT2 structure minimally filled */
833 memset(&cf2, 0, sizeof(cf2));
834 cf2.cbSize = sizeof(CHARFORMAT2A);
835 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
836 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
837 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
838 ok(rc == FALSE, "Should not be able to undo here.\n");
839 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
840
841 /* A valid flag, CHARFORMAT2 structure minimally filled */
842 memset(&cf2, 0, sizeof(cf2));
843 cf2.cbSize = sizeof(CHARFORMAT2A);
844 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
845 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
846 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
847 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
848 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
849
850 /* A valid flag, CHARFORMAT2 structure minimally filled */
851 memset(&cf2, 0, sizeof(cf2));
852 cf2.cbSize = sizeof(CHARFORMAT2A);
853 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
854 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
855 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
856 ok(rc == TRUE, "Should not be able to undo here.\n");
857 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
858
859 cf2.cbSize = sizeof(CHARFORMAT2A);
860 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
861
862 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
863 cf2.cbSize = sizeof(CHARFORMAT2A);
864 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
865 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
866 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
867
868 /* wParam==0 is default char format, does not set modify */
869 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
870 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
871 ok(rc == 0, "Text marked as modified, expected not modified!\n");
872 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
873 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
874 if (! rtl)
875 {
876 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
877 ok(rc == 0, "Text marked as modified, expected not modified!\n");
878 }
879 else
880 skip("RTL language found\n");
881
882 /* wParam==SCF_SELECTION sets modify if nonempty selection */
883 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
884 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
885 ok(rc == 0, "Text marked as modified, expected not modified!\n");
886 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
887 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
888 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
889 ok(rc == 0, "Text marked as modified, expected not modified!\n");
890
891 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
892 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
893 ok(rc == 0, "Text marked as modified, expected not modified!\n");
894 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
895 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
896 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
897 ok(rc == 0, "Text marked as modified, expected not modified!\n");
898 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
899 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
900 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
901 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
902 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
903
904 /* wParam==SCF_ALL sets modify regardless of whether text is present */
905 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
906 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
907 ok(rc == 0, "Text marked as modified, expected not modified!\n");
908 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
909 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
910 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
911 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
912
913 DestroyWindow(hwndRichEdit);
914
915 /* EM_GETCHARFORMAT tests */
916 for (i = 0; tested_effects[i]; i++)
917 {
918 hwndRichEdit = new_richedit(NULL);
919 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
920
921 /* Need to set a TrueType font to get consistent CFM_BOLD results */
922 memset(&cf2, 0, sizeof(CHARFORMAT2A));
923 cf2.cbSize = sizeof(CHARFORMAT2A);
924 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
925 cf2.dwEffects = 0;
926 strcpy(cf2.szFaceName, "Courier New");
927 cf2.wWeight = FW_DONTCARE;
928 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
929
930 memset(&cf2, 0, sizeof(CHARFORMAT2A));
931 cf2.cbSize = sizeof(CHARFORMAT2A);
932 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
933 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
934 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
935 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
936 ||
937 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
938 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
939 ok((cf2.dwEffects & tested_effects[i]) == 0,
940 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
941
942 memset(&cf2, 0, sizeof(CHARFORMAT2A));
943 cf2.cbSize = sizeof(CHARFORMAT2A);
944 cf2.dwMask = tested_effects[i];
945 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
946 cf2.dwMask = CFM_SUPERSCRIPT;
947 cf2.dwEffects = tested_effects[i];
948 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
949 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
950
951 memset(&cf2, 0, sizeof(CHARFORMAT2A));
952 cf2.cbSize = sizeof(CHARFORMAT2A);
953 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
954 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
955 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
956 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
957 ||
958 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
959 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
960 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
961 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
962
963 memset(&cf2, 0, sizeof(CHARFORMAT2A));
964 cf2.cbSize = sizeof(CHARFORMAT2A);
965 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
966 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
967 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
968 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
969 ||
970 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
971 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
972 ok((cf2.dwEffects & tested_effects[i]) == 0,
973 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
974
975 memset(&cf2, 0, sizeof(CHARFORMAT2A));
976 cf2.cbSize = sizeof(CHARFORMAT2A);
977 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
978 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
979 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
980 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
981 ||
982 (cf2.dwMask & tested_effects[i]) == 0),
983 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
984
985 DestroyWindow(hwndRichEdit);
986 }
987
988 for (i = 0; tested_effects[i]; i++)
989 {
990 hwndRichEdit = new_richedit(NULL);
991 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
992
993 /* Need to set a TrueType font to get consistent CFM_BOLD results */
994 memset(&cf2, 0, sizeof(CHARFORMAT2A));
995 cf2.cbSize = sizeof(CHARFORMAT2A);
996 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
997 cf2.dwEffects = 0;
998 strcpy(cf2.szFaceName, "Courier New");
999 cf2.wWeight = FW_DONTCARE;
1000 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1001
1002 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1003 cf2.cbSize = sizeof(CHARFORMAT2A);
1004 cf2.dwMask = tested_effects[i];
1005 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1006 cf2.dwMask = CFM_SUPERSCRIPT;
1007 cf2.dwEffects = tested_effects[i];
1008 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1009 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1010
1011 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1012 cf2.cbSize = sizeof(CHARFORMAT2A);
1013 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1014 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1015 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1016 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1017 ||
1018 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1019 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1020 ok((cf2.dwEffects & tested_effects[i]) == 0,
1021 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1022
1023 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1024 cf2.cbSize = sizeof(CHARFORMAT2A);
1025 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1026 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1027 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1028 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1029 ||
1030 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1031 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1032 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1033 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1034
1035 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1036 cf2.cbSize = sizeof(CHARFORMAT2A);
1037 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1038 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1039 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1040 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1041 ||
1042 (cf2.dwMask & tested_effects[i]) == 0),
1043 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1044 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1045 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1046
1047 DestroyWindow(hwndRichEdit);
1048 }
1049
1050 /* Effects applied on an empty selection should take effect when selection is
1051 replaced with text */
1052 hwndRichEdit = new_richedit(NULL);
1053 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1054 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1055
1056 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1057 cf2.cbSize = sizeof(CHARFORMAT2A);
1058 cf2.dwMask = CFM_BOLD;
1059 cf2.dwEffects = CFE_BOLD;
1060 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1061
1062 /* Selection is now nonempty */
1063 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1064
1065 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1066 cf2.cbSize = sizeof(CHARFORMAT2A);
1067 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1068 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1069
1070 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1071 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1072 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1073 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1074
1075
1076 /* Set two effects on an empty selection */
1077 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1078 /* first clear bold, italic */
1079 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1080 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1081 cf2.cbSize = sizeof(CHARFORMAT2A);
1082 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1083 cf2.dwEffects = 0;
1084 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1085
1086 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1087
1088 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1089 cf2.cbSize = sizeof(CHARFORMAT2A);
1090 cf2.dwMask = CFM_BOLD;
1091 cf2.dwEffects = CFE_BOLD;
1092 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1093 cf2.dwMask = CFM_ITALIC;
1094 cf2.dwEffects = CFE_ITALIC;
1095 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1096
1097 /* Selection is now nonempty */
1098 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1099
1100 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1101 cf2.cbSize = sizeof(CHARFORMAT2A);
1102 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1103 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1104
1105 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1106 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1107 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1108 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1109
1110 /* Setting the (empty) selection to exactly the same place as before should
1111 NOT clear the insertion style! */
1112 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1113 /* first clear bold, italic */
1114 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1115 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1116 cf2.cbSize = sizeof(CHARFORMAT2A);
1117 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1118 cf2.dwEffects = 0;
1119 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1120
1121 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1122
1123 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1124 cf2.cbSize = sizeof(CHARFORMAT2A);
1125 cf2.dwMask = CFM_BOLD;
1126 cf2.dwEffects = CFE_BOLD;
1127 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1128
1129 /* Empty selection in same place, insert style should NOT be forgotten here. */
1130 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1131
1132 /* Selection is now nonempty */
1133 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1134
1135 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1136 cf2.cbSize = sizeof(CHARFORMAT2A);
1137 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1138 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1139
1140 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1141 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1142 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1143 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1144
1145 /* Moving the selection will clear the insertion style */
1146 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1147 /* first clear bold, italic */
1148 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1149 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1150 cf2.cbSize = sizeof(CHARFORMAT2A);
1151 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1152 cf2.dwEffects = 0;
1153 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1154
1155 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1156
1157 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1158 cf2.cbSize = sizeof(CHARFORMAT2A);
1159 cf2.dwMask = CFM_BOLD;
1160 cf2.dwEffects = CFE_BOLD;
1161 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1162
1163 /* Move selection and then put it back, insert style should be forgotten here. */
1164 SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1165 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1166
1167 /* Selection is now nonempty */
1168 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1169
1170 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1171 cf2.cbSize = sizeof(CHARFORMAT2A);
1172 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1173 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1174
1175 ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1176 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1177 ok((cf2.dwEffects & CFE_BOLD) == 0,
1178 "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1179
1180 /* Ditto with EM_EXSETSEL */
1181 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1182 /* first clear bold, italic */
1183 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1184 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1185 cf2.cbSize = sizeof(CHARFORMAT2A);
1186 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1187 cf2.dwEffects = 0;
1188 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1189
1190 cr.cpMin = 2; cr.cpMax = 2;
1191 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1192
1193 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1194 cf2.cbSize = sizeof(CHARFORMAT2A);
1195 cf2.dwMask = CFM_BOLD;
1196 cf2.dwEffects = CFE_BOLD;
1197 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1198
1199 /* Empty selection in same place, insert style should NOT be forgotten here. */
1200 cr.cpMin = 2; cr.cpMax = 2;
1201 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1202
1203 /* Selection is now nonempty */
1204 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1205
1206 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1207 cf2.cbSize = sizeof(CHARFORMAT2A);
1208 cr.cpMin = 2; cr.cpMax = 6;
1209 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1210 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1211
1212 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1213 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1214 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1215 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1216
1217 /* show that wWeight is at the correct offset in CHARFORMAT2A */
1218 memset(&cf2, 0, sizeof(cf2));
1219 cf2.cbSize = sizeof(cf2);
1220 cf2.dwMask = CFM_WEIGHT;
1221 cf2.wWeight = 100;
1222 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1223 memset(&cf2, 0, sizeof(cf2));
1224 cf2.cbSize = sizeof(cf2);
1225 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1226 ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1227
1228 memset(&cf2, 0, sizeof(cf2));
1229 cf2.cbSize = sizeof(cf2);
1230 cf2.dwMask = CFM_SPACING;
1231 cf2.sSpacing = 10;
1232 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1233 memset(&cf2, 0, sizeof(cf2));
1234 cf2.cbSize = sizeof(cf2);
1235 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1236 ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1237
1238 /* show that wWeight is at the correct offset in CHARFORMAT2W */
1239 memset(&cfW, 0, sizeof(cfW));
1240 cfW.cbSize = sizeof(cfW);
1241 cfW.dwMask = CFM_WEIGHT;
1242 cfW.wWeight = 100;
1243 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1244 memset(&cfW, 0, sizeof(cfW));
1245 cfW.cbSize = sizeof(cfW);
1246 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1247 ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1248
1249 memset(&cfW, 0, sizeof(cfW));
1250 cfW.cbSize = sizeof(cfW);
1251 cfW.dwMask = CFM_SPACING;
1252 cfW.sSpacing = 10;
1253 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1254 memset(&cfW, 0, sizeof(cfW));
1255 cfW.cbSize = sizeof(cfW);
1256 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1257 ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1258
1259 /* test CFE_UNDERLINE and bUnderlineType interaction */
1260 /* clear bold, italic */
1261 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1262 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1263 cf2.cbSize = sizeof(CHARFORMAT2A);
1264 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1265 cf2.dwEffects = 0;
1266 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1267
1268 /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1269 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1270 cf2.cbSize = sizeof(CHARFORMAT2A);
1271 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1272 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1273 "got %08x\n", cf2.dwMask);
1274 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1275 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1276
1277 /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1278 cf2.dwMask = CFM_UNDERLINETYPE;
1279 cf2.bUnderlineType = CFU_UNDERLINE;
1280 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1281 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1282 cf2.cbSize = sizeof(CHARFORMAT2A);
1283 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1284 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1285 "got %08x\n", cf2.dwMask);
1286 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1287 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1288
1289 /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1290 cf2.dwMask = CFM_UNDERLINETYPE;
1291 cf2.bUnderlineType = CFU_UNDERLINENONE;
1292 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1293 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1294 cf2.cbSize = sizeof(CHARFORMAT2A);
1295 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1296 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1297 "got %08x\n", cf2.dwMask);
1298 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1299 ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1300
1301 /* another underline type also sets CFE_UNDERLINE */
1302 cf2.dwMask = CFM_UNDERLINETYPE;
1303 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1304 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1305 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1306 cf2.cbSize = sizeof(CHARFORMAT2A);
1307 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1308 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1309 "got %08x\n", cf2.dwMask);
1310 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1311 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1312
1313 /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1314 cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1315 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1316 cf2.dwEffects = 0;
1317 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1318 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1319 cf2.cbSize = sizeof(CHARFORMAT2A);
1320 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1321 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1322 "got %08x\n", cf2.dwMask);
1323 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1324 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1325
1326 /* And turing it back on again by just setting CFE_UNDERLINE */
1327 cf2.dwMask = CFM_UNDERLINE;
1328 cf2.dwEffects = CFE_UNDERLINE;
1329 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1330 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1331 cf2.cbSize = sizeof(CHARFORMAT2A);
1332 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1333 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1334 "got %08x\n", cf2.dwMask);
1335 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1336 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1337
1338 DestroyWindow(hwndRichEdit);
1339 }
1340
1341 static void test_EM_SETTEXTMODE(void)
1342 {
1343 HWND hwndRichEdit = new_richedit(NULL);
1344 CHARFORMAT2A cf2, cf2test;
1345 CHARRANGE cr;
1346 int rc = 0;
1347
1348 /*Attempt to use mutually exclusive modes*/
1349 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1350 ok(rc == E_INVALIDARG,
1351 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1352
1353 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1354 /*Insert text into the control*/
1355
1356 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1357
1358 /*Attempt to change the control to plain text mode*/
1359 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1360 ok(rc == E_UNEXPECTED,
1361 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1362
1363 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1364 If rich text is pasted, it should have the same formatting as the rest
1365 of the text in the control*/
1366
1367 /*Italicize the text
1368 *NOTE: If the default text was already italicized, the test will simply
1369 reverse; in other words, it will copy a regular "wine" into a plain
1370 text window that uses an italicized format*/
1371 cf2.cbSize = sizeof(CHARFORMAT2A);
1372 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1373
1374 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1375 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1376
1377 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1378 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1379
1380 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1381 however, SCF_ALL has been implemented*/
1382 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1383 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1384
1385 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1386 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1387
1388 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1389
1390 /*Select the string "wine"*/
1391 cr.cpMin = 0;
1392 cr.cpMax = 4;
1393 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1394
1395 /*Copy the italicized "wine" to the clipboard*/
1396 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1397
1398 /*Reset the formatting to default*/
1399 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1400 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1401 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1402
1403 /*Clear the text in the control*/
1404 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1405
1406 /*Switch to Plain Text Mode*/
1407 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1408 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1409
1410 /*Input "wine" again in normal format*/
1411 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1412
1413 /*Paste the italicized "wine" into the control*/
1414 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1415
1416 /*Select a character from the first "wine" string*/
1417 cr.cpMin = 2;
1418 cr.cpMax = 3;
1419 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1420
1421 /*Retrieve its formatting*/
1422 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1423
1424 /*Select a character from the second "wine" string*/
1425 cr.cpMin = 5;
1426 cr.cpMax = 6;
1427 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1428
1429 /*Retrieve its formatting*/
1430 cf2test.cbSize = sizeof(CHARFORMAT2A);
1431 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1432
1433 /*Compare the two formattings*/
1434 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1435 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1436 cf2.dwEffects, cf2test.dwEffects);
1437 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1438 printing "wine" in the current format(normal)
1439 pasting "wine" from the clipboard(italicized)
1440 comparing the two formats(should differ)*/
1441
1442 /*Attempt to switch with text in control*/
1443 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1444 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1445
1446 /*Clear control*/
1447 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1448
1449 /*Switch into Rich Text mode*/
1450 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1451 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1452
1453 /*Print "wine" in normal formatting into the control*/
1454 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1455
1456 /*Paste italicized "wine" into the control*/
1457 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1458
1459 /*Select text from the first "wine" string*/
1460 cr.cpMin = 1;
1461 cr.cpMax = 3;
1462 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1463
1464 /*Retrieve its formatting*/
1465 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1466
1467 /*Select text from the second "wine" string*/
1468 cr.cpMin = 6;
1469 cr.cpMax = 7;
1470 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1471
1472 /*Retrieve its formatting*/
1473 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1474
1475 /*Test that the two formattings are not the same*/
1476 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1477 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1478 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1479
1480 DestroyWindow(hwndRichEdit);
1481 }
1482
1483 static void test_SETPARAFORMAT(void)
1484 {
1485 HWND hwndRichEdit = new_richedit(NULL);
1486 PARAFORMAT2 fmt;
1487 HRESULT ret;
1488 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1489 fmt.cbSize = sizeof(PARAFORMAT2);
1490 fmt.dwMask = PFM_ALIGNMENT;
1491 fmt.wAlignment = PFA_LEFT;
1492
1493 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1494 ok(ret != 0, "expected non-zero got %d\n", ret);
1495
1496 fmt.cbSize = sizeof(PARAFORMAT2);
1497 fmt.dwMask = -1;
1498 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1499 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1500 * between richedit different native builds of riched20.dll
1501 * used on different Windows versions. */
1502 ret &= ~PFM_TABLEROWDELIMITER;
1503 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1504
1505 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1506 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1507
1508 /* Test some other paraformat field defaults */
1509 ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1510 ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1511 ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1512 ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1513
1514 DestroyWindow(hwndRichEdit);
1515 }
1516
1517 static void test_TM_PLAINTEXT(void)
1518 {
1519 /*Tests plain text properties*/
1520
1521 HWND hwndRichEdit = new_richedit(NULL);
1522 CHARFORMAT2A cf2, cf2test;
1523 CHARRANGE cr;
1524 int rc = 0;
1525
1526 /*Switch to plain text mode*/
1527
1528 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1529 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1530
1531 /*Fill control with text*/
1532
1533 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1534
1535 /*Select some text and bold it*/
1536
1537 cr.cpMin = 10;
1538 cr.cpMax = 20;
1539 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1540 cf2.cbSize = sizeof(CHARFORMAT2A);
1541 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1542
1543 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1544 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1545
1546 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1547 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1548
1549 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1550 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1551
1552 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1553 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1554
1555 /*Get the formatting of those characters*/
1556
1557 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1558
1559 /*Get the formatting of some other characters*/
1560 cf2test.cbSize = sizeof(CHARFORMAT2A);
1561 cr.cpMin = 21;
1562 cr.cpMax = 30;
1563 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1564 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1565
1566 /*Test that they are the same as plain text allows only one formatting*/
1567
1568 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1569 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1570 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1571
1572 /*Fill the control with a "wine" string, which when inserted will be bold*/
1573
1574 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1575
1576 /*Copy the bolded "wine" string*/
1577
1578 cr.cpMin = 0;
1579 cr.cpMax = 4;
1580 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1581 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1582
1583 /*Swap back to rich text*/
1584
1585 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1586 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1587
1588 /*Set the default formatting to bold italics*/
1589
1590 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1591 cf2.dwMask |= CFM_ITALIC;
1592 cf2.dwEffects ^= CFE_ITALIC;
1593 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1594 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1595
1596 /*Set the text in the control to "wine", which will be bold and italicized*/
1597
1598 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1599
1600 /*Paste the plain text "wine" string, which should take the insert
1601 formatting, which at the moment is bold italics*/
1602
1603 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1604
1605 /*Select the first "wine" string and retrieve its formatting*/
1606
1607 cr.cpMin = 1;
1608 cr.cpMax = 3;
1609 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1610 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1611
1612 /*Select the second "wine" string and retrieve its formatting*/
1613
1614 cr.cpMin = 5;
1615 cr.cpMax = 7;
1616 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1617 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1618
1619 /*Compare the two formattings. They should be the same.*/
1620
1621 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1622 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1623 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1624 DestroyWindow(hwndRichEdit);
1625 }
1626
1627 static void test_WM_GETTEXT(void)
1628 {
1629 HWND hwndRichEdit = new_richedit(NULL);
1630 static const char text[] = "Hello. My name is RichEdit!";
1631 static const char text2[] = "Hello. My name is RichEdit!\r";
1632 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1633 char buffer[1024] = {0};
1634 int result;
1635
1636 /* Baseline test with normal-sized buffer */
1637 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1638 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1639 ok(result == lstrlenA(buffer),
1640 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1641 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1642 result = strcmp(buffer,text);
1643 ok(result == 0,
1644 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1645
1646 /* Test for returned value of WM_GETTEXTLENGTH */
1647 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1648 ok(result == lstrlenA(text),
1649 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1650 result, lstrlenA(text));
1651
1652 /* Test for behavior in overflow case */
1653 memset(buffer, 0, 1024);
1654 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1655 ok(result == 0 ||
1656 result == lstrlenA(text) - 1, /* XP, win2k3 */
1657 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1658 result = strcmp(buffer,text);
1659 if (result)
1660 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1661 ok(result == 0,
1662 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1663
1664 /* Baseline test with normal-sized buffer and carriage return */
1665 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1666 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1667 ok(result == lstrlenA(buffer),
1668 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1669 result = strcmp(buffer,text2_after);
1670 ok(result == 0,
1671 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1672
1673 /* Test for returned value of WM_GETTEXTLENGTH */
1674 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1675 ok(result == lstrlenA(text2_after),
1676 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1677 result, lstrlenA(text2_after));
1678
1679 /* Test for behavior of CRLF conversion in case of overflow */
1680 memset(buffer, 0, 1024);
1681 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1682 ok(result == 0 ||
1683 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1684 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1685 result = strcmp(buffer,text2);
1686 if (result)
1687 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1688 ok(result == 0,
1689 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1690
1691 DestroyWindow(hwndRichEdit);
1692 }
1693
1694 static void test_EM_GETTEXTRANGE(void)
1695 {
1696 HWND hwndRichEdit = new_richedit(NULL);
1697 const char * text1 = "foo bar\r\nfoo bar";
1698 const char * text2 = "foo bar\rfoo bar";
1699 const char * expect = "bar\rfoo";
1700 char buffer[1024] = {0};
1701 LRESULT result;
1702 TEXTRANGEA textRange;
1703
1704 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1705
1706 textRange.lpstrText = buffer;
1707 textRange.chrg.cpMin = 4;
1708 textRange.chrg.cpMax = 11;
1709 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1710 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1711 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1712
1713 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1714
1715 textRange.lpstrText = buffer;
1716 textRange.chrg.cpMin = 4;
1717 textRange.chrg.cpMax = 11;
1718 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1719 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1720 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1721
1722 /* cpMax of text length is used instead of -1 in this case */
1723 textRange.lpstrText = buffer;
1724 textRange.chrg.cpMin = 0;
1725 textRange.chrg.cpMax = -1;
1726 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1727 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1728 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1729
1730 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1731 textRange.lpstrText = buffer;
1732 textRange.chrg.cpMin = -1;
1733 textRange.chrg.cpMax = 1;
1734 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1735 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1736 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1737
1738 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1739 textRange.lpstrText = buffer;
1740 textRange.chrg.cpMin = 1;
1741 textRange.chrg.cpMax = -1;
1742 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1743 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1744 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1745
1746 /* no end character is copied if cpMax - cpMin < 0 */
1747 textRange.lpstrText = buffer;
1748 textRange.chrg.cpMin = 5;
1749 textRange.chrg.cpMax = 5;
1750 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1751 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1752 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1753
1754 /* cpMax of text length is used if cpMax > text length*/
1755 textRange.lpstrText = buffer;
1756 textRange.chrg.cpMin = 0;
1757 textRange.chrg.cpMax = 1000;
1758 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1759 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1760 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1761
1762 /* Test with multibyte character */
1763 if (!is_lang_japanese)
1764 skip("Skip multibyte character tests on non-Japanese platform\n");
1765 else
1766 {
1767 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1768 textRange.chrg.cpMin = 4;
1769 textRange.chrg.cpMax = 8;
1770 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1771 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1772 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1773 }
1774
1775 DestroyWindow(hwndRichEdit);
1776 }
1777
1778 static void test_EM_GETSELTEXT(void)
1779 {
1780 HWND hwndRichEdit = new_richedit(NULL);
1781 const char * text1 = "foo bar\r\nfoo bar";
1782 const char * text2 = "foo bar\rfoo bar";
1783 const char * expect = "bar\rfoo";
1784 char buffer[1024] = {0};
1785 LRESULT result;
1786
1787 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1788
1789 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1790 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1791 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1792 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1793
1794 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1795
1796 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1797 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1798 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1799 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1800
1801 /* Test with multibyte character */
1802 if (!is_lang_japanese)
1803 skip("Skip multibyte character tests on non-Japanese platform\n");
1804 else
1805 {
1806 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1807 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1808 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1809 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1810 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1811 }
1812
1813 DestroyWindow(hwndRichEdit);
1814 }
1815
1816 /* FIXME: need to test unimplemented options and robustly test wparam */
1817 static void test_EM_SETOPTIONS(void)
1818 {
1819 HWND hwndRichEdit;
1820 static const char text[] = "Hello. My name is RichEdit!";
1821 char buffer[1024] = {0};
1822 DWORD dwStyle, options, oldOptions;
1823 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1824 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1825 ES_SELECTIONBAR|ES_VERTICAL;
1826
1827 /* Test initial options. */
1828 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1829 0, 0, 200, 60, NULL, NULL,
1830 hmoduleRichEdit, NULL);
1831 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1832 RICHEDIT_CLASS20A, (int) GetLastError());
1833 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1834 ok(options == 0, "Incorrect initial options %x\n", options);
1835 DestroyWindow(hwndRichEdit);
1836
1837 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1838 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1839 0, 0, 200, 60, NULL, NULL,
1840 hmoduleRichEdit, NULL);
1841 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1842 RICHEDIT_CLASS20A, (int) GetLastError());
1843 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1844 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1845 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1846 "Incorrect initial options %x\n", options);
1847
1848 /* NEGATIVE TESTING - NO OPTIONS SET */
1849 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1850 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1851
1852 /* testing no readonly by sending 'a' to the control*/
1853 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1854 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1855 ok(buffer[0]=='a',
1856 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1857 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1858
1859 /* READONLY - sending 'a' to the control */
1860 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1861 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1862 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1863 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1864 ok(buffer[0]==text[0],
1865 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1866
1867 /* EM_SETOPTIONS changes the window style, but changing the
1868 * window style does not change the options. */
1869 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1870 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1871 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1872 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1873 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1874 /* Confirm that the text is still read only. */
1875 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1876 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1877 ok(buffer[0]==text[0],
1878 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1879
1880 oldOptions = options;
1881 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1882 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1883 ok(options == oldOptions,
1884 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1885
1886 DestroyWindow(hwndRichEdit);
1887 }
1888
1889 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1890 {
1891 CHARFORMAT2A text_format;
1892 text_format.cbSize = sizeof(text_format);
1893 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1894 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1895 return (text_format.dwEffects & CFE_LINK) != 0;
1896 }
1897
1898 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1899 {
1900 BOOL link_present = FALSE;
1901
1902 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1903 if (is_url)
1904 { /* control text is url; should get CFE_LINK */
1905 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1906 }
1907 else
1908 {
1909 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1910 }
1911 }
1912
1913 static HWND new_static_wnd(HWND parent) {
1914 return new_window("Static", 0, parent);
1915 }
1916
1917 static void test_EM_AUTOURLDETECT(void)
1918 {
1919 /* DO NOT change the properties of the first two elements. To shorten the
1920 tests, all tests after WM_SETTEXT test just the first two elements -
1921 one non-URL and one URL */
1922 struct urls_s {
1923 const char *text;
1924 BOOL is_url;
1925 } urls[12] = {
1926 {"winehq.org", FALSE},
1927 {"http://www.winehq.org", TRUE},
1928 {"http//winehq.org", FALSE},
1929 {"ww.winehq.org", FALSE},
1930 {"www.winehq.org", TRUE},
1931 {"ftp://192.168.1.1", TRUE},
1932 {"ftp//192.168.1.1", FALSE},
1933 {"mailto:your@email.com", TRUE},
1934 {"prospero:prosperoserver", TRUE},
1935 {"telnet:test", TRUE},
1936 {"news:newserver", TRUE},
1937 {"wais:waisserver", TRUE}
1938 };
1939
1940 int i, j;
1941 int urlRet=-1;
1942 HWND hwndRichEdit, parent;
1943
1944 /* All of the following should cause the URL to be detected */
1945 const char * templates_delim[] = {
1946 "This is some text with X on it",
1947 "This is some text with (X) on it",
1948 "This is some text with X\r on it",
1949 "This is some text with ---X--- on it",
1950 "This is some text with \"X\" on it",
1951 "This is some text with 'X' on it",
1952 "This is some text with 'X' on it",
1953 "This is some text with :X: on it",
1954
1955 "This text ends with X",
1956
1957 "This is some text with X) on it",
1958 "This is some text with X--- on it",
1959 "This is some text with X\" on it",
1960 "This is some text with X' on it",
1961 "This is some text with X: on it",
1962
1963 "This is some text with (X on it",
1964 "This is some text with \rX on it",
1965 "This is some text with ---X on it",
1966 "This is some text with \"X on it",
1967 "This is some text with 'X on it",
1968 "This is some text with :X on it",
1969 };
1970 /* None of these should cause the URL to be detected */
1971 const char * templates_non_delim[] = {
1972 "This is some text with |X| on it",
1973 "This is some text with *X* on it",
1974 "This is some text with /X/ on it",
1975 "This is some text with +X+ on it",
1976 "This is some text with %X% on it",
1977 "This is some text with #X# on it",
1978 "This is some text with @X@ on it",
1979 "This is some text with \\X\\ on it",
1980 "This is some text with |X on it",
1981 "This is some text with *X on it",
1982 "This is some text with /X on it",
1983 "This is some text with +X on it",
1984 "This is some text with %X on it",
1985 "This is some text with #X on it",
1986 "This is some text with @X on it",
1987 "This is some text with \\X on it",
1988 "This is some text with _X on it",
1989 };
1990 /* All of these cause the URL detection to be extended by one more byte,
1991 thus demonstrating that the tested character is considered as part
1992 of the URL. */
1993 const char * templates_xten_delim[] = {
1994 "This is some text with X| on it",
1995 "This is some text with X* on it",
1996 "This is some text with X/ on it",
1997 "This is some text with X+ on it",
1998 "This is some text with X% on it",
1999 "This is some text with X# on it",
2000 "This is some text with X@ on it",
2001 "This is some text with X\\ on it",
2002 "This is some text with X_ on it",
2003 };
2004 /* These delims act as neutral breaks. Whether the url is ended
2005 or not depends on the next non-neutral character. We'll test
2006 with Y unchanged, in which case the url should include the
2007 deliminator and the Y. We'll also test with the Y changed
2008 to a space, in which case the url stops before the
2009 deliminator. */
2010 const char * templates_neutral_delim[] = {
2011 "This is some text with X-Y on it",
2012 "This is some text with X--Y on it",
2013 "This is some text with X!Y on it",
2014 "This is some text with X[Y on it",
2015 "This is some text with X]Y on it",
2016 "This is some text with X{Y on it",
2017 "This is some text with X}Y on it",
2018 "This is some text with X(Y on it",
2019 "This is some text with X)Y on it",
2020 "This is some text with X\"Y on it",
2021 "This is some text with X;Y on it",
2022 "This is some text with X:Y on it",
2023 "This is some text with X'Y on it",
2024 "This is some text with X?Y on it",
2025 "This is some text with X<Y on it",
2026 "This is some text with X>Y on it",
2027 "This is some text with X.Y on it",
2028 "This is some text with X,Y on it",
2029 };
2030 char buffer[1024];
2031
2032 parent = new_static_wnd(NULL);
2033 hwndRichEdit = new_richedit(parent);
2034 /* Try and pass EM_AUTOURLDETECT some test wParam values */
2035 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2036 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2037 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2038 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2039 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2040 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2041 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2042 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2043 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2044 /* for each url, check the text to see if CFE_LINK effect is present */
2045 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2046
2047 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2048 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2049 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2050
2051 /* Link detection should happen immediately upon WM_SETTEXT */
2052 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2053 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2054 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2055 }
2056 DestroyWindow(hwndRichEdit);
2057
2058 /* Test detection of URLs within normal text - WM_SETTEXT case. */
2059 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2060 hwndRichEdit = new_richedit(parent);
2061
2062 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2063 char * at_pos;
2064 int at_offset;
2065 int end_offset;
2066
2067 at_pos = strchr(templates_delim[j], 'X');
2068 at_offset = at_pos - templates_delim[j];
2069 memcpy(buffer, templates_delim[j], at_offset);
2070 buffer[at_offset] = '\0';
2071 strcat(buffer, urls[i].text);
2072 strcat(buffer, templates_delim[j] + at_offset + 1);
2073 end_offset = at_offset + strlen(urls[i].text);
2074
2075 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2077
2078 /* This assumes no templates start with the URL itself, and that they
2079 have at least two characters before the URL text */
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2082 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2083 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2084 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2085 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2086
2087 if (urls[i].is_url)
2088 {
2089 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2090 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2091 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2092 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2093 }
2094 else
2095 {
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2100 }
2101 if (buffer[end_offset] != '\0')
2102 {
2103 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2104 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2105 if (buffer[end_offset +1] != '\0')
2106 {
2107 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2108 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2109 }
2110 }
2111 }
2112
2113 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
2114 char * at_pos;
2115 int at_offset;
2116 int end_offset;
2117
2118 at_pos = strchr(templates_non_delim[j], 'X');
2119 at_offset = at_pos - templates_non_delim[j];
2120 memcpy(buffer, templates_non_delim[j], at_offset);
2121 buffer[at_offset] = '\0';
2122 strcat(buffer, urls[i].text);
2123 strcat(buffer, templates_non_delim[j] + at_offset + 1);
2124 end_offset = at_offset + strlen(urls[i].text);
2125
2126 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2128
2129 /* This assumes no templates start with the URL itself, and that they
2130 have at least two characters before the URL text */
2131 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2132 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2137
2138 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2139 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2140 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2141 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2142 if (buffer[end_offset] != '\0')
2143 {
2144 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2145 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2146 if (buffer[end_offset +1] != '\0')
2147 {
2148 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2149 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2150 }
2151 }
2152 }
2153
2154 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
2155 char * at_pos;
2156 int at_offset;
2157 int end_offset;
2158
2159 at_pos = strchr(templates_xten_delim[j], 'X');
2160 at_offset = at_pos - templates_xten_delim[j];
2161 memcpy(buffer, templates_xten_delim[j], at_offset);
2162 buffer[at_offset] = '\0';
2163 strcat(buffer, urls[i].text);
2164 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2165 end_offset = at_offset + strlen(urls[i].text);
2166
2167 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2168 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2169
2170 /* This assumes no templates start with the URL itself, and that they
2171 have at least two characters before the URL text */
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2174 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2175 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2178
2179 if (urls[i].is_url)
2180 {
2181 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2183 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2185 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2186 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2187 }
2188 else
2189 {
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2192 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2193 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2194 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2195 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2196 }
2197 if (buffer[end_offset +1] != '\0')
2198 {
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2201 if (buffer[end_offset +2] != '\0')
2202 {
2203 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2204 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2205 }
2206 }
2207 }
2208
2209 for (j = 0; j < sizeof(templates_neutral_delim) / sizeof(const char *); j++) {
2210 char * at_pos, * end_pos;
2211 int at_offset;
2212 int end_offset;
2213
2214 if (!urls[i].is_url) continue;
2215
2216 at_pos = strchr(templates_neutral_delim[j], 'X');
2217 at_offset = at_pos - templates_neutral_delim[j];
2218 memcpy(buffer, templates_neutral_delim[j], at_offset);
2219 buffer[at_offset] = '\0';
2220 strcat(buffer, urls[i].text);
2221 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2222
2223 end_pos = strchr(buffer, 'Y');
2224 end_offset = end_pos - buffer;
2225
2226 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2227 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2228
2229 /* This assumes no templates start with the URL itself, and that they
2230 have at least two characters before the URL text */
2231 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2232 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2233 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2234 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2237
2238 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2239 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2240 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2241 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2242 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2243 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2244
2245 *end_pos = ' ';
2246
2247 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2248 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2249
2250 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2251 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2253 "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2254 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2255 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2256 }
2257
2258 DestroyWindow(hwndRichEdit);
2259 hwndRichEdit = NULL;
2260 }
2261
2262 /* Test detection of URLs within normal text - WM_CHAR case. */
2263 /* Test only the first two URL examples for brevity */
2264 for (i = 0; i < 2; i++) {
2265 hwndRichEdit = new_richedit(parent);
2266
2267 /* Also for brevity, test only the first three delimiters */
2268 for (j = 0; j < 3; j++) {
2269 char * at_pos;
2270 int at_offset;
2271 int end_offset;
2272 int u, v;
2273
2274 at_pos = strchr(templates_delim[j], 'X');
2275 at_offset = at_pos - templates_delim[j];
2276 end_offset = at_offset + strlen(urls[i].text);
2277
2278 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2279 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2280 for (u = 0; templates_delim[j][u]; u++) {
2281 if (templates_delim[j][u] == '\r') {
2282 simulate_typing_characters(hwndRichEdit, "\r");
2283 } else if (templates_delim[j][u] != 'X') {
2284 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2285 } else {
2286 for (v = 0; urls[i].text[v]; v++) {
2287 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2288 }
2289 }
2290 }
2291 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2292
2293 /* This assumes no templates start with the URL itself, and that they
2294 have at least two characters before the URL text */
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2299 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2300 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2301
2302 if (urls[i].is_url)
2303 {
2304 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2305 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2306 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2307 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2308 }
2309 else
2310 {
2311 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2312 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2313 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2314 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2315 }
2316 if (buffer[end_offset] != '\0')
2317 {
2318 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2319 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2320 if (buffer[end_offset +1] != '\0')
2321 {
2322 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2323 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2324 }
2325 }
2326
2327 /* The following will insert a paragraph break after the first character
2328 of the URL candidate, thus breaking the URL. It is expected that the
2329 CFE_LINK attribute should break across both pieces of the URL */
2330 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2331 simulate_typing_characters(hwndRichEdit, "\r");
2332 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2333
2334 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2335 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2336 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2337 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2338 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2339 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2340
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2343 /* end_offset moved because of paragraph break */
2344 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2345 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2346 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2347 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2348 {
2349 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2350 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2351 if (buffer[end_offset +2] != '\0')
2352 {
2353 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2354 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2355 }
2356 }
2357
2358 /* The following will remove the just-inserted paragraph break, thus
2359 restoring the URL */
2360 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2361 simulate_typing_characters(hwndRichEdit, "\b");
2362 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2363
2364 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2365 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2366 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2367 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2368 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2369 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2370
2371 if (urls[i].is_url)
2372 {
2373 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2374 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2375 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2376 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2377 }
2378 else
2379 {
2380 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2381 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2382 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2383 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2384 }
2385 if (buffer[end_offset] != '\0')
2386 {
2387 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2388 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2389 if (buffer[end_offset +1] != '\0')
2390 {
2391 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2392 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2393 }
2394 }
2395 }
2396 DestroyWindow(hwndRichEdit);
2397 hwndRichEdit = NULL;
2398 }
2399
2400 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2401 /* Test just the first two URL examples for brevity */
2402 for (i = 0; i < 2; i++) {
2403 SETTEXTEX st;
2404
2405 hwndRichEdit = new_richedit(parent);
2406
2407 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2408 be detected:
2409 1) Set entire text, a la WM_SETTEXT
2410 2) Set a selection of the text to the URL
2411 3) Set a portion of the text at a time, which eventually results in
2412 an URL
2413 All of them should give equivalent results
2414 */
2415
2416 /* Set entire text in one go, like WM_SETTEXT */
2417 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2418 char * at_pos;
2419 int at_offset;
2420 int end_offset;
2421
2422 st.codepage = CP_ACP;
2423 st.flags = ST_DEFAULT;
2424
2425 at_pos = strchr(templates_delim[j], 'X');
2426 at_offset = at_pos - templates_delim[j];
2427 memcpy(buffer, templates_delim[j], at_offset);
2428 buffer[at_offset] = '\0';
2429 strcat(buffer, urls[i].text);
2430 strcat(buffer, templates_delim[j] + at_offset + 1);
2431 end_offset = at_offset + strlen(urls[i].text);
2432
2433 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2434 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2435
2436 /* This assumes no templates start with the URL itself, and that they
2437 have at least two characters before the URL text */
2438 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2439 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2440 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2441 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2442 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2443 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2444
2445 if (urls[i].is_url)
2446 {
2447 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2448 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2449 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2450 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2451 }
2452 else
2453 {
2454 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2455 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2456 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2457 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2458 }
2459 if (buffer[end_offset] != '\0')
2460 {
2461 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2462 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2463 if (buffer[end_offset +1] != '\0')
2464 {
2465 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2466 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2467 }
2468 }
2469 }
2470
2471 /* Set selection with X to the URL */
2472 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2473 char * at_pos;
2474 int at_offset;
2475 int end_offset;
2476
2477 at_pos = strchr(templates_delim[j], 'X');
2478 at_offset = at_pos - templates_delim[j];
2479 end_offset = at_offset + strlen(urls[i].text);
2480
2481 st.codepage = CP_ACP;
2482 st.flags = ST_DEFAULT;
2483 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2484 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2485 st.flags = ST_SELECTION;
2486 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2487 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2488 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2489
2490 /* This assumes no templates start with the URL itself, and that they
2491 have at least two characters before the URL text */
2492 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2493 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2494 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2495 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2496 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2497 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2498
2499 if (urls[i].is_url)
2500 {
2501 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2502 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2503 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2504 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2505 }
2506 else
2507 {
2508 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2509 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2510 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2511 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2512 }
2513 if (buffer[end_offset] != '\0')
2514 {
2515 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2516 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2517 if (buffer[end_offset +1] != '\0')
2518 {
2519 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2520 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2521 }
2522 }
2523 }
2524
2525 /* Set selection with X to the first character of the URL, then the rest */
2526 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2527 char * at_pos;
2528 int at_offset;
2529 int end_offset;
2530
2531 at_pos = strchr(templates_delim[j], 'X');
2532 at_offset = at_pos - templates_delim[j];
2533 end_offset = at_offset + strlen(urls[i].text);
2534
2535 strcpy(buffer, "YY");
2536 buffer[0] = urls[i].text[0];
2537
2538 st.codepage = CP_ACP;
2539 st.flags = ST_DEFAULT;
2540 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2541 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2542 st.flags = ST_SELECTION;
2543 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2544 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2545 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2546 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2547 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2548
2549 /* This assumes no templates start with the URL itself, and that they
2550 have at least two characters before the URL text */
2551 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2552 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2553 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2554 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2555 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2556 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2557
2558 if (urls[i].is_url)
2559 {
2560 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2561 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2562 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2563 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2564 }
2565 else
2566 {
2567 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2568 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2569 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2570 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2571 }
2572 if (buffer[end_offset] != '\0')
2573 {
2574 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2576 if (buffer[end_offset +1] != '\0')
2577 {
2578 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2579 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2580 }
2581 }
2582 }
2583
2584 DestroyWindow(hwndRichEdit);
2585 hwndRichEdit = NULL;
2586 }
2587
2588 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2589 /* Test just the first two URL examples for brevity */
2590 for (i = 0; i < 2; i++) {
2591 hwndRichEdit = new_richedit(parent);
2592
2593 /* Set selection with X to the URL */
2594 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2595 char * at_pos;
2596 int at_offset;
2597 int end_offset;
2598
2599 at_pos = strchr(templates_delim[j], 'X');
2600 at_offset = at_pos - templates_delim[j];
2601 end_offset = at_offset + strlen(urls[i].text);
2602
2603 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2604 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2605 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2606 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2607 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2608
2609 /* This assumes no templates start with the URL itself, and that they
2610 have at least two characters before the URL text */
2611 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2612 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2613 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2615 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2616 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2617
2618 if (urls[i].is_url)
2619 {
2620 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2621 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2622 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2623 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2624 }
2625 else
2626 {
2627 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2628 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2629 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2630 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2631 }
2632 if (buffer[end_offset] != '\0')
2633 {
2634 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2635 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2636 if (buffer[end_offset +1] != '\0')
2637 {
2638 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2640 }
2641 }
2642 }
2643
2644 /* Set selection with X to the first character of the URL, then the rest */
2645 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2646 char * at_pos;
2647 int at_offset;
2648 int end_offset;
2649
2650 at_pos = strchr(templates_delim[j], 'X');
2651 at_offset = at_pos - templates_delim[j];
2652 end_offset = at_offset + strlen(urls[i].text);
2653
2654 strcpy(buffer, "YY");
2655 buffer[0] = urls[i].text[0];
2656
2657 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2658 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2659 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2660 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2661 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2662 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2663 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2664
2665 /* This assumes no templates start with the URL itself, and that they
2666 have at least two characters before the URL text */
2667 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2668 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2669 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2670 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2671 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2672 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2673
2674 if (urls[i].is_url)
2675 {
2676 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2677 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2678 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2679 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2680 }
2681 else
2682 {
2683 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2684 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2685 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2687 }
2688 if (buffer[end_offset] != '\0')
2689 {
2690 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2692 if (buffer[end_offset +1] != '\0')
2693 {
2694 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2696 }
2697 }
2698 }
2699
2700 DestroyWindow(hwndRichEdit);
2701 hwndRichEdit = NULL;
2702 }
2703
2704 DestroyWindow(parent);
2705 }
2706
2707 static void test_EM_SCROLL(void)
2708 {
2709 int i, j;
2710 int r; /* return value */
2711 int expr; /* expected return value */
2712 HWND hwndRichEdit = new_richedit(NULL);
2713 int y_before, y_after; /* units of lines of text */
2714
2715 /* test a richedit box containing a single line of text */
2716 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2717 expr = 0x00010000;
2718 for (i = 0; i < 4; i++) {
2719 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2720
2721 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2722 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2723 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2724 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2725 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2726 "(i == %d)\n", y_after, i);
2727 }
2728
2729 /*
2730 * test a richedit box that will scroll. There are two general
2731 * cases: the case without any long lines and the case with a long
2732 * line.
2733 */
2734 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2735 if (i == 0)
2736 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2737 else
2738 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2739 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2740 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2741 "LONG LINE \nb\nc\nd\ne");
2742 for (j = 0; j < 12; j++) /* reset scroll position to top */
2743 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2744
2745 /* get first visible line */
2746 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2747 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2748
2749 /* get new current first visible line */
2750 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2751
2752 ok(((r & 0xffffff00) == 0x00010000) &&
2753 ((r & 0x000000ff) != 0x00000000),
2754 "EM_SCROLL page down didn't scroll by a small positive number of "
2755 "lines (r == 0x%08x)\n", r);
2756 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2757 "(line %d scrolled to line %d\n", y_before, y_after);
2758
2759 y_before = y_after;
2760
2761 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2762 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2763 ok(((r & 0xffffff00) == 0x0001ff00),
2764 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2765 "(r == 0x%08x)\n", r);
2766 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2767 "%d scrolled to line %d\n", y_before, y_after);
2768
2769 y_before = y_after;
2770
2771 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2772
2773 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2774
2775 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2776 "(r == 0x%08x)\n", r);
2777 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2778 "1 line (%d scrolled to %d)\n", y_before, y_after);
2779
2780 y_before = y_after;
2781
2782 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2783
2784 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2785
2786 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2787 "(r == 0x%08x)\n", r);
2788 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2789 "line (%d scrolled to %d)\n", y_before, y_after);
2790
2791 y_before = y_after;
2792
2793 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2794 SB_LINEUP, 0); /* lineup beyond top */
2795
2796 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2797
2798 ok(r == 0x00010000,
2799 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2800 ok(y_before == y_after,
2801 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2802
2803 y_before = y_after;
2804
2805 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2806 SB_PAGEUP, 0);/*page up beyond top */
2807
2808 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2809
2810 ok(r == 0x00010000,
2811 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2812 ok(y_before == y_after,
2813 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2814
2815 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2816 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2817 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2818 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2819 SB_PAGEDOWN, 0); /* page down beyond bot */
2820 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2821
2822 ok(r == 0x00010000,
2823 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2824 ok(y_before == y_after,
2825 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2826 y_before, y_after);
2827
2828 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2829 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2830 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2831
2832 ok(r == 0x00010000,
2833 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2834 ok(y_before == y_after,
2835 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2836 y_before, y_after);
2837 }
2838 DestroyWindow(hwndRichEdit);
2839 }
2840
2841 static unsigned int recursionLevel = 0;
2842 static unsigned int WM_SIZE_recursionLevel = 0;
2843 static BOOL bailedOutOfRecursion = FALSE;
2844 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2845
2846 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2847 {
2848 LRESULT r;
2849
2850 if (bailedOutOfRecursion) return 0;
2851 if (recursionLevel >= 32) {
2852 bailedOutOfRecursion = TRUE;
2853 return 0;
2854 }
2855
2856 recursionLevel++;
2857 switch (message) {
2858 case WM_SIZE:
2859 WM_SIZE_recursionLevel++;
2860 r = richeditProc(hwnd, message, wParam, lParam);
2861 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2862 ShowScrollBar(hwnd, SB_VERT, TRUE);
2863 WM_SIZE_recursionLevel--;
2864 break;
2865 default:
2866 r = richeditProc(hwnd, message, wParam, lParam);
2867 break;
2868 }
2869 recursionLevel--;
2870 return r;
2871 }
2872
2873 static void test_scrollbar_visibility(void)
2874 {
2875 HWND hwndRichEdit;
2876 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2877 SCROLLINFO si;
2878 WNDCLASSA cls;
2879 BOOL r;
2880
2881 /* These tests show that richedit should temporarily refrain from automatically
2882 hiding or showing its scrollbars (vertical at least) when an explicit request
2883 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2884 Some applications depend on forced showing (when otherwise richedit would
2885 hide the vertical scrollbar) and are thrown on an endless recursive loop
2886 if richedit auto-hides the scrollbar again. Apparently they never heard of
2887 the ES_DISABLENOSCROLL style... */
2888
2889 hwndRichEdit = new_richedit(NULL);
2890
2891 /* Test default scrollbar visibility behavior */
2892 memset(&si, 0, sizeof(si));
2893 si.cbSize = sizeof(si);
2894 si.fMask = SIF_PAGE | SIF_RANGE;
2895 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2896 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2897 "Vertical scrollbar is visible, should be invisible.\n");
2898 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2899 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2900 si.nPage, si.nMin, si.nMax);
2901
2902 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2903 memset(&si, 0, sizeof(si));
2904 si.cbSize = sizeof(si);
2905 si.fMask = SIF_PAGE | SIF_RANGE;
2906 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2907 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2908 "Vertical scrollbar is visible, should be invisible.\n");
2909 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2910 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2911 si.nPage, si.nMin, si.nMax);
2912
2913 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2914 memset(&si, 0, sizeof(si));
2915 si.cbSize = sizeof(si);
2916 si.fMask = SIF_PAGE | SIF_RANGE;
2917 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2918 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2919 "Vertical scrollbar is invisible, should be visible.\n");
2920 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2921 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2922 si.nPage, si.nMin, si.nMax);
2923
2924 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2925 even though it hides the scrollbar */
2926 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2927 memset(&si, 0, sizeof(si));
2928 si.cbSize = sizeof(si);
2929 si.fMask = SIF_PAGE | SIF_RANGE;
2930 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2931 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2932 "Vertical scrollbar is visible, should be invisible.\n");
2933 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2934 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2935 si.nPage, si.nMin, si.nMax);
2936
2937 /* Setting non-scrolling text again does *not* reset scrollbar range */
2938 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2939 memset(&si, 0, sizeof(si));
2940 si.cbSize = sizeof(si);
2941 si.fMask = SIF_PAGE | SIF_RANGE;
2942 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2943 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2944 "Vertical scrollbar is visible, should be invisible.\n");
2945 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2946 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2947 si.nPage, si.nMin, si.nMax);
2948
2949 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950 memset(&si, 0, sizeof(si));
2951 si.cbSize = sizeof(si);
2952 si.fMask = SIF_PAGE | SIF_RANGE;
2953 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955 "Vertical scrollbar is visible, should be invisible.\n");
2956 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958 si.nPage, si.nMin, si.nMax);
2959
2960 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2961 memset(&si, 0, sizeof(si));
2962 si.cbSize = sizeof(si);
2963 si.fMask = SIF_PAGE | SIF_RANGE;
2964 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2965 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2966 "Vertical scrollbar is visible, should be invisible.\n");
2967 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2968 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2969 si.nPage, si.nMin, si.nMax);
2970
2971 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2972 memset(&si, 0, sizeof(si));
2973 si.cbSize = sizeof(si);
2974 si.fMask = SIF_PAGE | SIF_RANGE;
2975 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2976 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2977 "Vertical scrollbar is visible, should be invisible.\n");
2978 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2979 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2980 si.nPage, si.nMin, si.nMax);
2981
2982 DestroyWindow(hwndRichEdit);
2983
2984 /* Test again, with ES_DISABLENOSCROLL style */
2985 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2986
2987 /* Test default scrollbar visibility behavior */
2988 memset(&si, 0, sizeof(si));
2989 si.cbSize = sizeof(si);
2990 si.fMask = SIF_PAGE | SIF_RANGE;
2991 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2992 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2993 "Vertical scrollbar is invisible, should be visible.\n");
2994 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2995 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2996 si.nPage, si.nMin, si.nMax);
2997
2998 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2999 memset(&si, 0, sizeof(si));
3000 si.cbSize = sizeof(si);
3001 si.fMask = SIF_PAGE | SIF_RANGE;
3002 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3003 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3004 "Vertical scrollbar is invisible, should be visible.\n");
3005 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3006 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3007 si.nPage, si.nMin, si.nMax);
3008
3009 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3010 memset(&si, 0, sizeof(si));
3011 si.cbSize = sizeof(si);
3012 si.fMask = SIF_PAGE | SIF_RANGE;
3013 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3014 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3015 "Vertical scrollbar is invisible, should be visible.\n");
3016 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3017 "reported page/range is %d (%d..%d)\n",
3018 si.nPage, si.nMin, si.nMax);
3019
3020 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3021 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3022 memset(&si, 0, sizeof(si));
3023 si.cbSize = sizeof(si);
3024 si.fMask = SIF_PAGE | SIF_RANGE;
3025 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3027 "Vertical scrollbar is invisible, should be visible.\n");
3028 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3029 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3030 si.nPage, si.nMin, si.nMax);
3031
3032 /* Setting non-scrolling text again does *not* reset scrollbar range */
3033 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3034 memset(&si, 0, sizeof(si));
3035 si.cbSize = sizeof(si);
3036 si.fMask = SIF_PAGE | SIF_RANGE;
3037 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3038 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3039 "Vertical scrollbar is invisible, should be visible.\n");
3040 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3041 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3042 si.nPage, si.nMin, si.nMax);
3043
3044 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3045 memset(&si, 0, sizeof(si));
3046 si.cbSize = sizeof(si);
3047 si.fMask = SIF_PAGE | SIF_RANGE;
3048 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050 "Vertical scrollbar is invisible, should be visible.\n");
3051 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3052 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3053 si.nPage, si.nMin, si.nMax);
3054
3055 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3056 memset(&si, 0, sizeof(si));
3057 si.cbSize = sizeof(si);
3058 si.fMask = SIF_PAGE | SIF_RANGE;
3059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3060 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3061 "Vertical scrollbar is invisible, should be visible.\n");
3062 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3063 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3064 si.nPage, si.nMin, si.nMax);
3065
3066 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3067 memset(&si, 0, sizeof(si));
3068 si.cbSize = sizeof(si);
3069 si.fMask = SIF_PAGE | SIF_RANGE;
3070 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3071 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3072 "Vertical scrollbar is invisible, should be visible.\n");
3073 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3074 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3075 si.nPage, si.nMin, si.nMax);
3076
3077 DestroyWindow(hwndRichEdit);
3078
3079 /* Test behavior with explicit visibility request, using ShowScrollBar() */
3080 hwndRichEdit = new_richedit(NULL);
3081
3082 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3083 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3084 memset(&si, 0, sizeof(si));
3085 si.cbSize = sizeof(si);
3086 si.fMask = SIF_PAGE | SIF_RANGE;
3087 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3088 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3089 "Vertical scrollbar is invisible, should be visible.\n");
3090 todo_wine {
3091 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3092 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3093 si.nPage, si.nMin, si.nMax);
3094 }
3095
3096 /* Ditto, see above */
3097 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3098 memset(&si, 0, sizeof(si));
3099 si.cbSize = sizeof(si);
3100 si.fMask = SIF_PAGE | SIF_RANGE;
3101 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3102 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3103 "Vertical scrollbar is invisible, should be visible.\n");
3104 todo_wine {
3105 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3106 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3107 si.nPage, si.nMin, si.nMax);
3108 }
3109
3110 /* Ditto, see above */
3111 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3112 memset(&si, 0, sizeof(si));
3113 si.cbSize = sizeof(si);
3114 si.fMask = SIF_PAGE | SIF_RANGE;
3115 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3116 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3117 "Vertical scrollbar is invisible, should be visible.\n");
3118 todo_wine {
3119 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3120 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3121 si.nPage, si.nMin, si.nMax);
3122 }
3123
3124 /* Ditto, see above */
3125 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3126 memset(&si, 0, sizeof(si));
3127 si.cbSize = sizeof(si);
3128 si.fMask = SIF_PAGE | SIF_RANGE;
3129 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3130 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3131 "Vertical scrollbar is invisible, should be visible.\n");
3132 todo_wine {
3133 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3134 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3135 si.nPage, si.nMin, si.nMax);
3136 }
3137
3138 /* Ditto, see above */
3139 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3140 memset(&si, 0, sizeof(si));
3141 si.cbSize = sizeof(si);
3142 si.fMask = SIF_PAGE | SIF_RANGE;
3143 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3144 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3145 "Vertical scrollbar is invisible, should be visible.\n");
3146 todo_wine {
3147 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3148 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3149 si.nPage, si.nMin, si.nMax);
3150 }
3151
3152 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3153 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3154 memset(&si, 0, sizeof(si));
3155 si.cbSize = sizeof(si);
3156 si.fMask = SIF_PAGE | SIF_RANGE;
3157 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3158 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3159 "Vertical scrollbar is visible, should be invisible.\n");
3160 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3161 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3162 si.nPage, si.nMin, si.nMax);
3163
3164 DestroyWindow(hwndRichEdit);
3165
3166 hwndRichEdit = new_richedit(NULL);
3167
3168 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3169 memset(&si, 0, sizeof(si));
3170 si.cbSize = sizeof(si);
3171 si.fMask = SIF_PAGE | SIF_RANGE;
3172 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3173 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3174 "Vertical scrollbar is visible, should be invisible.\n");
3175 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3176 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3177 si.nPage, si.nMin, si.nMax);
3178
3179 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3180 memset(&si, 0, sizeof(si));
3181 si.cbSize = sizeof(si);
3182 si.fMask = SIF_PAGE | SIF_RANGE;
3183 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3184 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3185 "Vertical scrollbar is visible, should be invisible.\n");
3186 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3187 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3188 si.nPage, si.nMin, si.nMax);
3189
3190 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3191 memset(&si, 0, sizeof(si));
3192 si.cbSize = sizeof(si);
3193 si.fMask = SIF_PAGE | SIF_RANGE;
3194 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3195 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3196 "Vertical scrollbar is visible, should be invisible.\n");
3197 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3198 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3199 si.nPage, si.nMin, si.nMax);
3200
3201 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3202 memset(&si, 0, sizeof(si));
3203 si.cbSize = sizeof(si);
3204 si.fMask = SIF_PAGE | SIF_RANGE;
3205 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3206 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3207 "Vertical scrollbar is visible, should be invisible.\n");
3208 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3209 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3210 si.nPage, si.nMin, si.nMax);
3211
3212 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3213 memset(&si, 0, sizeof(si));
3214 si.cbSize = sizeof(si);
3215 si.fMask = SIF_PAGE | SIF_RANGE;
3216 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3217 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3218 "Vertical scrollbar is invisible, should be visible.\n");
3219 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3220 "reported page/range is %d (%d..%d)\n",
3221 si.nPage, si.nMin, si.nMax);
3222
3223 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3224 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3225 memset(&si, 0, sizeof(si));
3226 si.cbSize = sizeof(si);
3227 si.fMask = SIF_PAGE | SIF_RANGE;
3228 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3229 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3230 "Vertical scrollbar is visible, should be invisible.\n");
3231 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3232 "reported page/range is %d (%d..%d)\n",
3233 si.nPage, si.nMin, si.nMax);
3234
3235 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3236 memset(&si, 0, sizeof(si));
3237 si.cbSize = sizeof(si);
3238 si.fMask = SIF_PAGE | SIF_RANGE;
3239 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3240 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3241 "Vertical scrollbar is visible, should be invisible.\n");
3242 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3243 "reported page/range is %d (%d..%d)\n",
3244 si.nPage, si.nMin, si.nMax);
3245
3246 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3247 EM_SCROLL will make visible any forcefully invisible scrollbar */
3248 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3249 memset(&si, 0, sizeof(si));
3250 si.cbSize = sizeof(si);
3251 si.fMask = SIF_PAGE | SIF_RANGE;
3252 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3253 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3254 "Vertical scrollbar is invisible, should be visible.\n");
3255 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3256 "reported page/range is %d (%d..%d)\n",
3257 si.nPage, si.nMin, si.nMax);
3258
3259 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3260 memset(&si, 0, sizeof(si));
3261 si.cbSize = sizeof(si);
3262 si.fMask = SIF_PAGE | SIF_RANGE;
3263 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3264 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3265 "Vertical scrollbar is visible, should be invisible.\n");
3266 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3267 "reported page/range is %d (%d..%d)\n",
3268 si.nPage, si.nMin, si.nMax);
3269
3270 /* Again, EM_SCROLL, with SB_LINEUP */
3271 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3272 memset(&si, 0, sizeof(si));
3273 si.cbSize = sizeof(si);
3274 si.fMask = SIF_PAGE | SIF_RANGE;
3275 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3276 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3277 "Vertical scrollbar is invisible, should be visible.\n");
3278 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3279 "reported page/range is %d (%d..%d)\n",
3280 si.nPage, si.nMin, si.nMax);
3281
3282 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3283 memset(&si, 0, sizeof(si));
3284 si.cbSize = sizeof(si);
3285 si.fMask = SIF_PAGE | SIF_RANGE;
3286 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3287 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3288 "Vertical scrollbar is visible, should be invisible.\n");
3289 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3290 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3291 si.nPage, si.nMin, si.nMax);
3292
3293 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3294 memset(&si, 0, sizeof(si));
3295 si.cbSize = sizeof(si);
3296 si.fMask = SIF_PAGE | SIF_RANGE;
3297 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3298 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3299 "Vertical scrollbar is invisible, should be visible.\n");
3300 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3301 "reported page/range is %d (%d..%d)\n",
3302 si.nPage, si.nMin, si.nMax);
3303
3304 DestroyWindow(hwndRichEdit);
3305
3306
3307 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3308 hwndRichEdit = new_richedit(NULL);
3309
3310 #define ENABLE_WS_VSCROLL(hwnd) \
3311 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3312 #define DISABLE_WS_VSCROLL(hwnd) \
3313 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3314
3315 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3316 ENABLE_WS_VSCROLL(hwndRichEdit);
3317 memset(&si, 0, sizeof(si));
3318 si.cbSize = sizeof(si);
3319 si.fMask = SIF_PAGE | SIF_RANGE;
3320 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3321 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3322 "Vertical scrollbar is invisible, should be visible.\n");
3323 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3324 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3325 si.nPage, si.nMin, si.nMax);
3326
3327 /* Ditto, see above */
3328 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3329 memset(&si, 0, sizeof(si));
3330 si.cbSize = sizeof(si);
3331 si.fMask = SIF_PAGE | SIF_RANGE;
3332 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3333 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3334 "Vertical scrollbar is invisible, should be visible.\n");
3335 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3336 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3337 si.nPage, si.nMin, si.nMax);
3338
3339 /* Ditto, see above */
3340 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3341 memset(&si, 0, sizeof(si));
3342 si.cbSize = sizeof(si);
3343 si.fMask = SIF_PAGE | SIF_RANGE;
3344 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3345 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3346 "Vertical scrollbar is invisible, should be visible.\n");
3347 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3348 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3349 si.nPage, si.nMin, si.nMax);
3350
3351 /* Ditto, see above */
3352 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3353 memset(&si, 0, sizeof(si));
3354 si.cbSize = sizeof(si);
3355 si.fMask = SIF_PAGE | SIF_RANGE;
3356 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3357 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3358 "Vertical scrollbar is invisible, should be visible.\n");
3359 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3360 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3361 si.nPage, si.nMin, si.nMax);
3362
3363 /* Ditto, see above */
3364 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3365 memset(&si, 0, sizeof(si));
3366 si.cbSize = sizeof(si);
3367 si.fMask = SIF_PAGE | SIF_RANGE;
3368 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3369 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3370 "Vertical scrollbar is invisible, should be visible.\n");
3371 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3372 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3373 si.nPage, si.nMin, si.nMax);
3374
3375 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3376 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3377 memset(&si, 0, sizeof(si));
3378 si.cbSize = sizeof(si);
3379 si.fMask = SIF_PAGE | SIF_RANGE;
3380 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3381 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3382 "Vertical scrollbar is visible, should be invisible.\n");
3383 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3384 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3385 si.nPage, si.nMin, si.nMax);
3386
3387 DestroyWindow(hwndRichEdit);
3388
3389 hwndRichEdit = new_richedit(NULL);
3390
3391 DISABLE_WS_VSCROLL(hwndRichEdit);
3392 memset(&si, 0, sizeof(si));
3393 si.cbSize = sizeof(si);
3394 si.fMask = SIF_PAGE | SIF_RANGE;
3395 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3396 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3397 "Vertical scrollbar is visible, should be invisible.\n");
3398 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3399 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3400 si.nPage, si.nMin, si.nMax);
3401
3402 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3403 memset(&si, 0, sizeof(si));
3404 si.cbSize = sizeof(si);
3405 si.fMask = SIF_PAGE | SIF_RANGE;
3406 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3407 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3408 "Vertical scrollbar is visible, should be invisible.\n");
3409 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3410 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3411 si.nPage, si.nMin, si.nMax);
3412
3413 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3414 memset(&si, 0, sizeof(si));
3415 si.cbSize = sizeof(si);
3416 si.fMask = SIF_PAGE | SIF_RANGE;
3417 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3418 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3419 "Vertical scrollbar is visible, should be invisible.\n");
3420 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3421 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3422 si.nPage, si.nMin, si.nMax);
3423
3424 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3425 memset(&si, 0, sizeof(si));
3426 si.cbSize = sizeof(si);
3427 si.fMask = SIF_PAGE | SIF_RANGE;
3428 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3429 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3430 "Vertical scrollbar is visible, should be invisible.\n");
3431 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3432 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3433 si.nPage, si.nMin, si.nMax);
3434
3435 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3436 memset(&si, 0, sizeof(si));
3437 si.cbSize = sizeof(si);
3438 si.fMask = SIF_PAGE | SIF_RANGE;
3439 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3440 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3441 "Vertical scrollbar is invisible, should be visible.\n");
3442 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3443 "reported page/range is %d (%d..%d)\n",
3444 si.nPage, si.nMin, si.nMax);
3445
3446 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3447 DISABLE_WS_VSCROLL(hwndRichEdit);
3448 memset(&si, 0, sizeof(si));
3449 si.cbSize = sizeof(si);
3450 si.fMask = SIF_PAGE | SIF_RANGE;
3451 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3452 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3453 "Vertical scrollbar is visible, should be invisible.\n");
3454 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3455 "reported page/range is %d (%d..%d)\n",
3456 si.nPage, si.nMin, si.nMax);
3457
3458 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3459 memset(&si, 0, sizeof(si));
3460 si.cbSize = sizeof(si);
3461 si.fMask = SIF_PAGE | SIF_RANGE;
3462 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3463 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3464 "Vertical scrollbar is visible, should be invisible.\n");
3465 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3466 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3467 si.nPage, si.nMin, si.nMax);
3468
3469 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3470 memset(&si, 0, sizeof(si));
3471 si.cbSize = sizeof(si);
3472 si.fMask = SIF_PAGE | SIF_RANGE;
3473 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3474 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3475 "Vertical scrollbar is invisible, should be visible.\n");
3476 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3477 "reported page/range is %d (%d..%d)\n",
3478 si.nPage, si.nMin, si.nMax);
3479
3480 DISABLE_WS_VSCROLL(hwndRichEdit);
3481 memset(&si, 0, sizeof(si));
3482 si.cbSize = sizeof(si);
3483 si.fMask = SIF_PAGE | SIF_RANGE;
3484 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3485 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3486 "Vertical scrollbar is visible, should be invisible.\n");
3487 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3488 "reported page/range is %d (%d..%d)\n",
3489 si.nPage, si.nMin, si.nMax);
3490
3491 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3492 EM_SCROLL will make visible any forcefully invisible scrollbar */
3493 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3494 memset(&si, 0, sizeof(si));
3495 si.cbSize = sizeof(si);
3496 si.fMask = SIF_PAGE | SIF_RANGE;
3497 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3498 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3499 "Vertical scrollbar is invisible, should be visible.\n");
3500 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3501 "reported page/range is %d (%d..%d)\n",
3502 si.nPage, si.nMin, si.nMax);
3503
3504 DISABLE_WS_VSCROLL(hwndRichEdit);
3505 memset(&si, 0, sizeof(si));
3506 si.cbSize = sizeof(si);
3507 si.fMask = SIF_PAGE | SIF_RANGE;
3508 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3509 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3510 "Vertical scrollbar is visible, should be invisible.\n");
3511 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3512 "reported page/range is %d (%d..%d)\n",
3513 si.nPage, si.nMin, si.nMax);
3514
3515 /* Again, EM_SCROLL, with SB_LINEUP */
3516 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3517 memset(&si, 0, sizeof(si));
3518 si.cbSize = sizeof(si);
3519 si.fMask = SIF_PAGE | SIF_RANGE;
3520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3522 "Vertical scrollbar is invisible, should be visible.\n");
3523 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3524 "reported page/range is %d (%d..%d)\n",
3525 si.nPage, si.nMin, si.nMax);
3526
3527 DestroyWindow(hwndRichEdit);
3528
3529 /* This window proc models what is going on with Corman Lisp 3.0.
3530 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3531 force the scrollbar into visibility. Recursion should NOT happen
3532 as a result of this action.
3533 */
3534 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3535 if (r) {
3536 richeditProc = cls.lpfnWndProc;
3537 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3538 cls.lpszClassName = "RicheditStupidOverride";
3539 if(!RegisterClassA(&cls)) assert(0);
3540
3541 recursionLevel = 0;
3542 WM_SIZE_recursionLevel = 0;
3543 bailedOutOfRecursion = FALSE;
3544 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3545 ok(!bailedOutOfRecursion,
3546 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3547
3548 recursionLevel = 0;
3549 WM_SIZE_recursionLevel = 0;
3550 bailedOutOfRecursion = FALSE;
3551 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3552 ok(!bailedOutOfRecursion,
3553 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3554
3555 /* Unblock window in order to process WM_DESTROY */
3556 recursionLevel = 0;
3557 bailedOutOfRecursion = FALSE;
3558 WM_SIZE_recursionLevel = 0;
3559 DestroyWindow(hwndRichEdit);
3560 }
3561 }
3562
3563 static void test_EM_SETUNDOLIMIT(void)
3564 {
3565 /* cases we test for:
3566 * default behaviour - limiting at 100 undo's
3567 * undo disabled - setting a limit of 0
3568 * undo limited - undo limit set to some to some number, like 2
3569 * bad input - sending a negative number should default to 100 undo's */
3570
3571 HWND hwndRichEdit = new_richedit(NULL);
3572 CHARRANGE cr;
3573 int i;
3574 int result;
3575
3576 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3577 cr.cpMin = 0;
3578 cr.cpMax = -1;
3579 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3580
3581 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3582 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3583 also, multiple pastes don't combine like WM_CHAR would */
3584
3585 /* first case - check the default */
3586 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3587 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3588 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3589 for (i=0; i<100; i++) /* Undo 100 of them */
3590 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3591 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3592 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3593
3594 /* second case - cannot undo */
3595 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3596 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3597 SendMessageA(hwndRichEdit,
3598 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3599 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3600 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3601
3602 /* third case - set it to an arbitrary number */
3603 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3604 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3605 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3606 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3607 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3608 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3609 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3610 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3611 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3612 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3613 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3614 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3615 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3616 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3617
3618 /* fourth case - setting negative numbers should default to 100 undos */
3619 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3620 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3621 ok (result == 100,
3622 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3623
3624 DestroyWindow(hwndRichEdit);
3625 }
3626
3627 static void test_ES_PASSWORD(void)
3628 {
3629 /* This isn't hugely testable, so we're just going to run it through its paces */
3630
3631 HWND hwndRichEdit = new_richedit(NULL);
3632 WCHAR result;
3633
3634 /* First, check the default of a regular control */
3635 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3636 ok (result == 0,
3637 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3638
3639 /* Now, set it to something normal */
3640 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3641 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3642 ok (result == 120,
3643 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3644
3645 /* Now, set it to something odd */
3646 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3647 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3648 ok (result == 1234,
3649 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3650 DestroyWindow(hwndRichEdit);
3651 }
3652
3653 LONG streamout_written = 0;
3654
3655 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3656 LPBYTE pbBuff,
3657 LONG cb,
3658 LONG *pcb)
3659 {
3660 char** str = (char**)dwCookie;
3661 *pcb = cb;
3662 if (*pcb > 0) {
3663 memcpy(*str, pbBuff, *pcb);
3664 *str += *pcb;
3665 }
3666 streamout_written = *pcb;
3667 return 0;
3668 }
3669
3670 static void test_WM_SETTEXT(void)
3671 {
3672 HWND hwndRichEdit = new_richedit(NULL);
3673 const char * TestItem1 = "TestSomeText";
3674 const char * TestItem2 = "TestSomeText\r";
3675 const char * TestItem2_after = "TestSomeText\r\n";
3676 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3677 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3678 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3679 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3680 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3681 const char * TestItem5_after = "TestSomeText TestSomeText";
3682 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3683 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3684 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3685 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3686
3687 const char rtftextA[] = "{\\rtf sometext}";
3688 const char urtftextA[] = "{\\urtf sometext}";
3689 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3690 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3691 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3692
3693 char buf[1024] = {0};
3694 WCHAR bufW[1024] = {0};
3695 LRESULT result;
3696
3697 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3698 any solitary \r to be converted to \r\n on return. Properly paired
3699 \r\n are not affected. It also shows that the special sequence \r\r\n
3700 gets converted to a single space.
3701 */
3702
3703 #define TEST_SETTEXT(a, b) \
3704 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3705 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3706 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3707 ok (result == lstrlenA(buf), \
3708 "WM_GETTEXT returned %ld instead of expected %u\n", \
3709 result, lstrlenA(buf)); \
3710 result = strcmp(b, buf); \
3711 ok(result == 0, \
3712 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3713
3714 TEST_SETTEXT(TestItem1, TestItem1)
3715 TEST_SETTEXT(TestItem2, TestItem2_after)
3716 TEST_SETTEXT(TestItem3, TestItem3_after)
3717 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3718 TEST_SETTEXT(TestItem4, TestItem4_after)
3719 TEST_SETTEXT(TestItem5, TestItem5_after)
3720 TEST_SETTEXT(TestItem6, TestItem6_after)
3721 TEST_SETTEXT(TestItem7, TestItem7_after)
3722
3723 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3724 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3725 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3726 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3727 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3728 DestroyWindow(hwndRichEdit);
3729 #undef TEST_SETTEXT
3730
3731 #define TEST_SETTEXTW(a, b) \
3732 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3733 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3734 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3735 ok (result == lstrlenW(bufW), \
3736 "WM_GETTEXT returned %ld instead of expected %u\n", \
3737 result, lstrlenW(bufW)); \
3738 result = lstrcmpW(b, bufW); \
3739 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3740
3741 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3742 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3743 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3744 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3745 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3746 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3747 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3748 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3749 DestroyWindow(hwndRichEdit);
3750 #undef TEST_SETTEXTW
3751 }
3752
3753 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3754 resent to the callkack. */
3755 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3756 LPBYTE pbBuff,
3757 LONG cb,
3758 LONG *pcb)
3759 {
3760 char** str = (char**)dwCookie;
3761 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3762 *pcb = 0;
3763 if (cb > 0) {
3764 memcpy(*str, pbBuff, cb);
3765 *str += cb;
3766 *pcb = 1;
3767 }
3768 return 0;
3769 }
3770
3771 static int count_pars(const char *buf)
3772 {
3773 const char *p = buf;
3774 int count = 0;
3775 while ((p = strstr( p, "\\par" )) != NULL)
3776 {
3777 if (!isalpha( p[4] ))
3778 count++;
3779 p++;
3780 }
3781 return count;
3782 }
3783
3784 static void test_EM_STREAMOUT(void)
3785 {
3786 HWND hwndRichEdit = new_richedit(NULL);
3787 int r;
3788 EDITSTREAM es;
3789 char buf[1024] = {0};
3790 char * p;
3791 LRESULT result;
3792
3793 const char * TestItem1 = "TestSomeText";
3794 const char * TestItem2 = "TestSomeText\r";
3795 const char * TestItem3 = "TestSomeText\r\n";
3796
3797 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3798 p = buf;
3799 es.dwCookie = (DWORD_PTR)&p;
3800 es.dwError = 0;
3801 es.pfnCallback = test_WM_SETTEXT_esCallback;
3802 memset(buf, 0, sizeof(buf));
3803 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3804 r = strlen(buf);
3805 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3806 ok(strcmp(buf, TestItem1) == 0,
3807 "streamed text different, got %s\n", buf);
3808 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3809
3810 /* RTF mode writes the final end of para \r if it's part of the selection */
3811 p = buf;
3812 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3813 ok (count_pars(buf) == 1, "got %s\n", buf);
3814 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3815 p = buf;
3816 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3817 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3818 ok (count_pars(buf) == 0, "got %s\n", buf);
3819 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3820 p = buf;
3821 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3822 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3823 ok (count_pars(buf) == 1, "got %s\n", buf);
3824 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3825
3826 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3827 p = buf;
3828 es.dwCookie = (DWORD_PTR)&p;
3829 es.dwError = 0;
3830 es.pfnCallback = test_WM_SETTEXT_esCallback;
3831 memset(buf, 0, sizeof(buf));
3832 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3833 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3834 r = strlen(buf);
3835 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3836 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3837 ok(strcmp(buf, TestItem3) == 0,
3838 "streamed text different from, got %s\n", buf);
3839
3840 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3841 p = buf;
3842 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3843 ok (count_pars(buf) == 2, "got %s\n", buf);
3844 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3845 p = buf;
3846 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3847 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3848 ok (count_pars(buf) == 1, "got %s\n", buf);
3849 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3850 p = buf;
3851 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3852 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3853 ok (count_pars(buf) == 2, "got %s\n", buf);
3854 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3855
3856 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3857 p = buf;
3858 es.dwCookie = (DWORD_PTR)&p;
3859 es.dwError = 0;
3860 es.pfnCallback = test_WM_SETTEXT_esCallback;
3861 memset(buf, 0, sizeof(buf));
3862 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3863 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3864 r = strlen(buf);
3865 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3866 ok(strcmp(buf, TestItem3) == 0,
3867 "streamed text different, got %s\n", buf);
3868
3869 /* Use a callback that sets *pcb to one */
3870 p = buf;
3871 es.dwCookie = (DWORD_PTR)&p;
3872 es.dwError = 0;
3873 es.pfnCallback = test_esCallback_written_1;
3874 memset(buf, 0, sizeof(buf));
3875 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3876 r = strlen(buf);
3877 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3878 ok(strcmp(buf, TestItem3) == 0,
3879 "streamed text different, got %s\n", buf);
3880 ok(result == 0, "got %ld expected 0\n", result);
3881
3882
3883 DestroyWindow(hwndRichEdit);
3884 }
3885
3886 static void test_EM_STREAMOUT_FONTTBL(void)
3887 {
3888 HWND hwndRichEdit = new_richedit(NULL);
3889 EDITSTREAM es;
3890 char buf[1024] = {0};
3891 char * p;
3892 char * fontTbl;
3893 int brackCount;
3894
3895 const char * TestItem = "TestSomeText";
3896
3897 /* fills in the richedit control with some text */
3898 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3899
3900 /* streams out the text in rtf format */
3901 p = buf;
3902 es.dwCookie = (DWORD_PTR)&p;
3903 es.dwError = 0;
3904 es.pfnCallback = test_WM_SETTEXT_esCallback;
3905 memset(buf, 0, sizeof(buf));
3906 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3907
3908 /* scans for \fonttbl, error if not found */
3909 fontTbl = strstr(buf, "\\fonttbl");
3910 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3911 if(fontTbl)
3912 {
3913 /* scans for terminating closing bracket */
3914 brackCount = 1;
3915 while(*fontTbl && brackCount)
3916 {
3917 if(*fontTbl == '{')
3918 brackCount++;
3919 else if(*fontTbl == '}')
3920 brackCount--;
3921 fontTbl++;
3922 }
3923 /* checks whether closing bracket is ok */
3924 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3925 if(!brackCount)
3926 {
3927 /* char before closing fonttbl block should be a closed bracket */
3928 fontTbl -= 2;
3929 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3930
3931 /* char after fonttbl block should be a crlf */
3932 fontTbl += 2;
3933 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3934 }
3935 }
3936 DestroyWindow(hwndRichEdit);
3937 }
3938
3939
3940 static void test_EM_SETTEXTEX(void)
3941 {
3942 HWND hwndRichEdit, parent;
3943 SCROLLINFO si;
3944 int sel_start, sel_end;
3945 SETTEXTEX setText;
3946 GETTEXTEX getText;
3947 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3948 'S', 'o', 'm', 'e',
3949 'T', 'e', 'x', 't', 0};
3950 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3951 't', 'S', 'o', 'm',
3952 'e', 'T', 'e', 'x',
3953 't', 't', 'S', 'o',
3954 'm', 'e', 'T', 'e',
3955 'x', 't', 0};
3956 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3957 '\r','t','S','o','m','e','T','e','x','t',0};
3958 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3959 'S', 'o', 'm', 'e',
3960 'T', 'e', 'x', 't',
3961 '\r', 0};
3962 const char * TestItem2_after = "TestSomeText\r\n";
3963 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3964 'S', 'o', 'm', 'e',
3965 'T', 'e', 'x', 't',
3966 '\r','\n','\r','\n', 0};
3967 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3968 'S', 'o', 'm', 'e',
3969 'T', 'e', 'x', 't',
3970 '\n','\n', 0};
3971 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3972 'S', 'o', 'm', 'e',
3973 'T', 'e', 'x', 't',
3974 '\r','\r', 0};
3975 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3976 'S', 'o', 'm', 'e',
3977 'T', 'e', 'x', 't',
3978 '\r','\r','\n','\r',
3979 '\n', 0};
3980 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3981 'S', 'o', 'm', 'e',
3982 'T', 'e', 'x', 't',
3983 ' ','\r', 0};
3984 #define MAX_BUF_LEN 1024
3985 WCHAR buf[MAX_BUF_LEN];
3986 char bufACP[MAX_BUF_LEN];
3987 char * p;
3988 int result;
3989 CHARRANGE cr;
3990 EDITSTREAM es;
3991 WNDCLASSA cls;
3992
3993 /* Test the scroll position with and without a parent window.
3994 *
3995 * For some reason the scroll position is 0 after EM_SETTEXTEX
3996 * with the ST_SELECTION flag only when the control has a parent
3997 * window, even though the selection is at the end. */
3998 cls.style = 0;
3999 cls.lpfnWndProc = DefWindowProcA;
4000 cls.cbClsExtra = 0;
4001 cls.cbWndExtra = 0;
4002 cls.hInstance = GetModuleHandleA(0);
4003 cls.hIcon = 0;
4004 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
4005 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4006 cls.lpszMenuName = NULL;
4007 cls.lpszClassName = "ParentTestClass";
4008 if(!RegisterClassA(&cls)) assert(0);
4009
4010 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4011 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4012 ok (parent != 0, "Failed to create parent window\n");
4013
4014 hwndRichEdit = CreateWindowExA(0,
4015 RICHEDIT_CLASS20A, NULL,
4016 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4017 0, 0, 200, 60, parent, NULL,
4018 hmoduleRichEdit, NULL);
4019
4020 setText.codepage = CP_ACP;
4021 setText.flags = ST_SELECTION;
4022 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4023 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4024 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4025 si.cbSize = sizeof(si);
4026 si.fMask = SIF_ALL;
4027 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4028 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4029 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4030 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4031 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4032
4033 DestroyWindow(parent);
4034
4035 /* Test without a parent window */
4036 hwndRichEdit = new_richedit(NULL);
4037 setText.codepage = CP_ACP;
4038 setText.flags = ST_SELECTION;
4039 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4040 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4041 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4042 si.cbSize = sizeof(si);
4043 si.fMask = SIF_ALL;
4044 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4045 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4046 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4047 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4048 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4049
4050 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4051 * but this time it is because the selection is at the beginning. */
4052 setText.codepage = CP_ACP;
4053 setText.flags = ST_DEFAULT;
4054 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4055 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4056 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4057 si.cbSize = sizeof(si);
4058 si.fMask = SIF_ALL;
4059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4060 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4061 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4062 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4063 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4064
4065 setText.codepage = 1200; /* no constant for unicode */
4066 getText.codepage = 1200; /* no constant for unicode */
4067 getText.cb = MAX_BUF_LEN;
4068 getText.flags = GT_DEFAULT;
4069 getText.lpDefaultChar = NULL;
4070 getText.lpUsedDefChar = NULL;
4071
4072 setText.flags = 0;
4073 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4074 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4075 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4076 ok(lstrcmpW(buf, TestItem1) == 0,
4077 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4078
4079 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4080 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4081 */
4082 setText.codepage = 1200; /* no constant for unicode */
4083 getText.codepage = 1200; /* no constant for unicode */
4084 getText.cb = MAX_BUF_LEN;
4085 getText.flags = GT_DEFAULT;
4086 getText.lpDefaultChar = NULL;
4087 getText.lpUsedDefChar = NULL;
4088 setText.flags = 0;
4089 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4090 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4091 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4092 ok(lstrcmpW(buf, TestItem2) == 0,
4093 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4094
4095 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4096 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4097 ok(strcmp((const char *)buf, TestItem2_after) == 0,
4098 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4099
4100 /* Baseline test for just-enough buffer space for string */
4101 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4102 getText.codepage = 1200; /* no constant for unicode */
4103 getText.flags = GT_DEFAULT;
4104 getText.lpDefaultChar = NULL;
4105 getText.lpUsedDefChar = NULL;
4106 memset(buf, 0, sizeof(buf));
4107 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4108 ok(lstrcmpW(buf, TestItem2) == 0,
4109 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4110
4111 /* When there is enough space for one character, but not both, of the CRLF
4112 pair at the end of the string, the CR is not copied at all. That is,
4113 the caller must not see CRLF pairs truncated to CR at the end of the
4114 string.
4115 */
4116 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4117 getText.codepage = 1200; /* no constant for unicode */
4118 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
4119 getText.lpDefaultChar = NULL;
4120 getText.lpUsedDefChar = NULL;
4121 memset(buf, 0, sizeof(buf));
4122 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4123 ok(lstrcmpW(buf, TestItem1) == 0,
4124 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4125
4126
4127 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4128 setText.codepage = 1200; /* no constant for unicode */
4129 getText.codepage = 1200; /* no constant for unicode */
4130 getText.cb = MAX_BUF_LEN;
4131 getText.flags = GT_DEFAULT;
4132 getText.lpDefaultChar = NULL;
4133 getText.lpUsedDefChar = NULL;
4134 setText.flags = 0;
4135 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4136 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4137 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4138 ok(lstrcmpW(buf, TestItem3_after) == 0,
4139 "EM_SETTEXTEX did not convert properly\n");
4140
4141 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4142 setText.codepage = 1200; /* no constant for unicode */
4143 getText.codepage = 1200; /* no constant for unicode */
4144 getText.cb = MAX_BUF_LEN;
4145 getText.flags = GT_DEFAULT;
4146 getText.lpDefaultChar = NULL;
4147 getText.lpUsedDefChar = NULL;
4148 setText.flags = 0;
4149 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4150 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4151 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4152 ok(lstrcmpW(buf, TestItem3_after) == 0,
4153 "EM_SETTEXTEX did not convert properly\n");
4154
4155 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4156 setText.codepage = 1200; /* no constant for unicode */
4157 getText.codepage = 1200; /* no constant for unicode */
4158 getText.cb = MAX_BUF_LEN;
4159 getText.flags = GT_DEFAULT;
4160 getText.lpDefaultChar = NULL;
4161 getText.lpUsedDefChar = NULL;
4162 setText.flags = 0;
4163 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4164 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4165 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4166 ok(lstrcmpW(buf, TestItem4_after) == 0,
4167 "EM_SETTEXTEX did not convert properly\n");
4168
4169 /* !ST_SELECTION && Unicode && !\rtf */
4170 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4171 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4172
4173 ok (result == 1,
4174 "EM_SETTEXTEX returned %d, instead of 1\n",result);
4175 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4176
4177 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4178 setText.flags = 0;
4179 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4180 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4181 /* select some text */
4182 cr.cpMax = 1;
4183 cr.cpMin = 3;
4184 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4185 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4186 setText.flags = ST_SELECTION;
4187 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4188 ok(result == 0,
4189 "EM_SETTEXTEX with NULL lParam to replace selection"
4190 " with no text should return 0. Got %i\n",
4191 result);
4192
4193 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4194 setText.flags = 0;
4195 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4196 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4197 /* select some text */
4198 cr.cpMax = 1;
4199 cr.cpMin = 3;
4200 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4201 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4202 setText.flags = ST_SELECTION;
4203 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4204 /* get text */
4205 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4206 ok(result == lstrlenW(TestItem1),
4207 "EM_SETTEXTEX with NULL lParam to replace selection"
4208 " with no text should return 0. Got %i\n",
4209 result);
4210 ok(lstrlenW(buf) == 22,
4211 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4212 lstrlenW(buf) );
4213
4214 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4215 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4216 p = (char *)buf;
4217 es.dwCookie = (DWORD_PTR)&p;
4218 es.dwError = 0;
4219 es.pfnCallback = test_WM_SETTEXT_esCallback;
4220 memset(buf, 0, sizeof(buf));
4221 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4222 (WPARAM)(SF_RTF), (LPARAM)&es);
4223 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4224
4225 /* !ST_SELECTION && !Unicode && \rtf */
4226 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4227 getText.codepage = 1200; /* no constant for unicode */
4228 getText.cb = MAX_BUF_LEN;
4229 getText.flags = GT_DEFAULT;
4230 getText.lpDefaultChar = NULL;
4231 getText.lpUsedDefChar = NULL;
4232
4233 setText.flags = 0;
4234 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4235 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4236 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4237 ok(lstrcmpW(buf, TestItem1) == 0,
4238 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4239
4240 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4241 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4242 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4243 getText.codepage = CP_ACP;
4244 getText.cb = MAX_BUF_LEN;
4245 getText.flags = GT_DEFAULT;
4246 getText.lpDefaultChar = NULL;
4247 getText.lpUsedDefChar = NULL;
4248
4249 setText.flags = ST_SELECTION;
4250 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4251 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4252 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4253 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4254 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4255
4256 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4257 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4258 p = (char *)buf;
4259 es.dwCookie = (DWORD_PTR)&p;
4260 es.dwError = 0;
4261 es.pfnCallback = test_WM_SETTEXT_esCallback;
4262 memset(buf, 0, sizeof(buf));
4263 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4264 (WPARAM)(SF_RTF), (LPARAM)&es);
4265 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4266
4267 /* select some text */
4268 cr.cpMax = 1;
4269 cr.cpMin = 3;
4270 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4271
4272 /* ST_SELECTION && !Unicode && \rtf */
4273 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4274 getText.codepage = 1200; /* no constant for unicode */
4275 getText.cb = MAX_BUF_LEN;
4276 getText.flags = GT_DEFAULT;
4277 getText.lpDefaultChar = NULL;
4278 getText.lpUsedDefChar = NULL;
4279
4280 setText.flags = ST_SELECTION;
4281 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4282 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4283 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4284
4285 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4286 setText.codepage = 1200; /* no constant for unicode */
4287 getText.codepage = CP_ACP;
4288 getText.cb = MAX_BUF_LEN;
4289
4290 setText.flags = 0;
4291 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4292 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4293
4294 /* select some text */
4295 cr.cpMax = 1;
4296 cr.cpMin = 3;
4297 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4298
4299 /* ST_SELECTION && !Unicode && !\rtf */
4300 setText.codepage = CP_ACP;
4301 getText.codepage = 1200; /* no constant for unicode */
4302 getText.cb = MAX_BUF_LEN;
4303 getText.flags = GT_DEFAULT;
4304 getText.lpDefaultChar = NULL;
4305 getText.lpUsedDefChar = NULL;
4306
4307 setText.flags = ST_SELECTION;
4308 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4309 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4310 ok(lstrcmpW(buf, TestItem1alt) == 0,
4311 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4312 " using ST_SELECTION and non-Unicode\n");
4313
4314 /* Test setting text using rich text format */
4315 setText.flags = 0;
4316 setText.codepage = CP_ACP;
4317 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4318 getText.codepage = CP_ACP;
4319 getText.cb = MAX_BUF_LEN;
4320 getText.flags = GT_DEFAULT;
4321 getText.lpDefaultChar = NULL;
4322 getText.lpUsedDefChar = NULL;
4323 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4324 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4325
4326 setText.flags = 0;
4327 setText.codepage = CP_ACP;
4328 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4329 getText.codepage = CP_ACP;
4330 getText.cb = MAX_BUF_LEN;
4331 getText.flags = GT_DEFAULT;
4332 getText.lpDefaultChar = NULL;
4333 getText.lpUsedDefChar = NULL;
4334 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4335 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4336
4337 /* test for utf8 text with BOM */
4338 setText.flags = 0;
4339 setText.codepage = CP_ACP;
4340 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4341 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4342 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4343 result = strcmp(bufACP, "TestUTF8WithBOM");
4344 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4345
4346 setText.flags = 0;
4347 setText.codepage = CP_UTF8;
4348 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4349 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4350 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4351 result = strcmp(bufACP, "TestUTF8WithBOM");
4352 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4353
4354 /* Test multibyte character */
4355 if (!is_lang_japanese)
4356 skip("Skip multibyte character tests on non-Japanese platform\n");
4357 else
4358 {
4359 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4360 setText.flags = ST_SELECTION;
4361 setText.codepage = CP_ACP;
4362 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4363 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4364 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4365 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4366 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4367 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4368
4369 setText.flags = ST_DEFAULT;
4370 setText.codepage = CP_ACP;
4371 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4372 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4373 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4374 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4375 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4376 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4377
4378 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4379 setText.flags = ST_SELECTION;
4380 setText.codepage = CP_ACP;
4381 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4382 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4383 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4384 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4385 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4386 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4387 }
4388
4389 DestroyWindow(hwndRichEdit);
4390 }
4391
4392 static void test_EM_LIMITTEXT(void)
4393 {
4394 int ret;
4395
4396 HWND hwndRichEdit = new_richedit(NULL);
4397
4398 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4399 * about setting the length to -1 for multiline edit controls doesn't happen.
4400 */
4401
4402 /* Don't check default gettextlimit case. That's done in other tests */
4403
4404 /* Set textlimit to 100 */
4405 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4406 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4407 ok (ret == 100,
4408 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4409
4410 /* Set textlimit to 0 */
4411 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4412 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4413 ok (ret == 65536,
4414 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4415
4416 /* Set textlimit to -1 */
4417 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4418 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4419 ok (ret == -1,
4420 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4421
4422 /* Set textlimit to -2 */
4423 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4424 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4425 ok (ret == -2,
4426 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4427
4428 DestroyWindow (hwndRichEdit);
4429 }
4430
4431
4432 static void test_EM_EXLIMITTEXT(void)
4433 {
4434 int i, selBegin, selEnd, len1, len2;
4435 int result;
4436 char text[1024 + 1];
4437 char buffer[1024 + 1];
4438 int textlimit = 0; /* multiple of 100 */
4439 HWND hwndRichEdit = new_richedit(NULL);
4440
4441 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4442 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4443
4444 textlimit = 256000;
4445 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4446 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4447 /* set higher */
4448 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4449
4450 textlimit = 1000;
4451 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4452 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4453 /* set lower */
4454 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4455
4456 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4457 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4458 /* default for WParam = 0 */
4459 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4460
4461 textlimit = sizeof(text)-1;
4462 memset(text, 'W', textlimit);
4463 text[sizeof(text)-1] = 0;
4464 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4465 /* maxed out text */
4466 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4467
4468 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4469 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4470 len1 = selEnd - selBegin;
4471
4472 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4473 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4474 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4475 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4476 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4477 len2 = selEnd - selBegin;
4478
4479 ok(len1 != len2,
4480 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4481 len1,len2,i);
4482
4483 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4484 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4485 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4486 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4487 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4488 len1 = selEnd - selBegin;
4489
4490 ok(len1 != len2,
4491 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4492 len1,len2,i);
4493
4494 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4495 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4496 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4497 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4498 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4499 len2 = selEnd - selBegin;
4500
4501 ok(len1 == len2,
4502 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4503 len1,len2,i);
4504
4505 /* set text up to the limit, select all the text, then add a char */
4506 textlimit = 5;
4507 memset(text, 'W', textlimit);
4508 text[textlimit] = 0;
4509 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4510 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4511 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4512 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4513 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4514 result = strcmp(buffer, "A");
4515 ok(0 == result, "got string = \"%s\"\n", buffer);
4516
4517 /* WM_SETTEXT not limited */
4518 textlimit = 10;
4519 memset(text, 'W', textlimit);
4520 text[textlimit] = 0;
4521 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4522 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4523 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4524 i = strlen(buffer);
4525 ok(10 == i, "expected 10 chars\n");
4526 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4527 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4528
4529 /* try inserting more text at end */
4530 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4531 ok(0 == i, "WM_CHAR wasn't processed\n");
4532 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4533 i = strlen(buffer);
4534 ok(10 == i, "expected 10 chars, got %i\n", i);
4535 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4536 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4537
4538 /* try inserting text at beginning */
4539 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4540 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4541 ok(0 == i, "WM_CHAR wasn't processed\n");
4542 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4543 i = strlen(buffer);
4544 ok(10 == i, "expected 10 chars, got %i\n", i);
4545 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4546 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4547
4548 /* WM_CHAR is limited */
4549 textlimit = 1;
4550 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4551 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4552 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4553 ok(0 == i, "WM_CHAR wasn't processed\n");
4554 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4555 ok(0 == i, "WM_CHAR wasn't processed\n");
4556 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4557 i = strlen(buffer);
4558 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4559
4560 DestroyWindow(hwndRichEdit);
4561 }
4562
4563 static void test_EM_GETLIMITTEXT(void)
4564 {
4565 int i;
4566 HWND hwndRichEdit = new_richedit(NULL);
4567
4568 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4569 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4570
4571 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4572 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4573 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4574
4575 DestroyWindow(hwndRichEdit);
4576 }
4577
4578 static void test_WM_SETFONT(void)
4579 {
4580 /* There is no invalid input or error conditions for this function.
4581 * NULL wParam and lParam just fall back to their default values
4582 * It should be noted that even if you use a gibberish name for your fonts
4583 * here, it will still work because the name is stored. They will display as
4584 * System, but will report their name to be whatever they were created as */
4585
4586 HWND hwndRichEdit = new_richedit(NULL);
4587 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4588 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4589 FF_DONTCARE, "Marlett");
4590 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4591 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4592 FF_DONTCARE, "MS Sans Serif");
4593 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4594 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4595 FF_DONTCARE, "Courier");
4596 LOGFONTA sentLogFont;
4597 CHARFORMAT2A returnedCF2A;
4598
4599 returnedCF2A.cbSize = sizeof(returnedCF2A);
4600
4601 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4602 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4603 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4604
4605 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4606 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4607 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4608 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4609
4610 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4611 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4612 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4613 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4614 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4615 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4616
4617 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4618 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4619 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4620 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4621 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4622 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4623
4624 /* This last test is special since we send in NULL. We clear the variables
4625 * and just compare to "System" instead of the sent in font name. */
4626 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4627 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4628 returnedCF2A.cbSize = sizeof(returnedCF2A);
4629
4630 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4631 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4632 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4633 ok (!strcmp("System",returnedCF2A.szFaceName),
4634 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4635
4636 DestroyWindow(hwndRichEdit);
4637 }
4638
4639
4640 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4641 LPBYTE pbBuff,
4642 LONG cb,
4643 LONG *pcb)
4644 {
4645 const char** str = (const char**)dwCookie;
4646 int size = strlen(*str);
4647 if(size > 3) /* let's make it piecemeal for fun */
4648 size = 3;
4649 *pcb = cb;
4650 if (*pcb > size) {
4651 *pcb = size;
4652 }
4653 if (*pcb > 0) {
4654 memcpy(pbBuff, *str, *pcb);
4655 *str += *pcb;
4656 }
4657 return 0;
4658 }
4659
4660 static void test_EM_GETMODIFY(void)
4661 {
4662 HWND hwndRichEdit = new_richedit(NULL);
4663 LRESULT result;
4664 SETTEXTEX setText;
4665 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4666 'S', 'o', 'm', 'e',
4667 'T', 'e', 'x', 't', 0};
4668 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4669 'S', 'o', 'm', 'e',
4670 'O', 't', 'h', 'e', 'r',
4671 'T', 'e', 'x', 't', 0};
4672 const char* streamText = "hello world";
4673 CHARFORMAT2A cf2;
4674 PARAFORMAT2 pf2;
4675 EDITSTREAM es;
4676
4677 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4678 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4679 FF_DONTCARE, "Courier");
4680
4681 setText.codepage = 1200; /* no constant for unicode */
4682 setText.flags = ST_KEEPUNDO;
4683
4684
4685 /* modify flag shouldn't be set when richedit is first created */
4686 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4687 ok (result == 0,
4688 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4689
4690 /* setting modify flag should actually set it */
4691 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4692 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4693 ok (result != 0,
4694 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4695
4696 /* clearing modify flag should actually clear it */
4697 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4698 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4699 ok (result == 0,
4700 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4701
4702 /* setting font doesn't change modify flag */
4703 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4704 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4705 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4706 ok (result == 0,
4707 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4708
4709 /* setting text should set modify flag */
4710 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4711 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4712 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4713 ok (result != 0,
4714 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4715
4716 /* undo previous text doesn't reset modify flag */
4717 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4718 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4719 ok (result != 0,
4720 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4721
4722 /* set text with no flag to keep undo stack should not set modify flag */
4723 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4724 setText.flags = 0;
4725 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4726 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4727 ok (result == 0,
4728 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4729
4730 /* WM_SETTEXT doesn't modify */
4731 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4732 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4733 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4734 ok (result == 0,
4735 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4736
4737 /* clear the text */
4738 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4739 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4740 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4741 ok (result == 0,
4742 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4743
4744 /* replace text */
4745 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4746 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4747 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4748 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4749 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4750 ok (result != 0,
4751 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4752
4753 /* copy/paste text 1 */
4754 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4755 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4756 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4757 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4758 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4759 ok (result != 0,
4760 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4761
4762 /* copy/paste text 2 */
4763 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4764 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4765 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4766 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4767 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4768 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4769 ok (result != 0,
4770 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4771
4772 /* press char */
4773 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4774 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4775 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4776 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4777 ok (result != 0,
4778 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4779
4780 /* press del */
4781 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4782 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4783 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4784 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4785 ok (result != 0,
4786 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4787
4788 /* set char format */
4789 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4790 cf2.cbSize = sizeof(CHARFORMAT2A);
4791 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4792 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4793 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4794 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4795 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4796 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4797 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4798 ok (result != 0,
4799 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4800
4801 /* set para format */
4802 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4803 pf2.cbSize = sizeof(PARAFORMAT2);
4804 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4805 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4806 pf2.wAlignment = PFA_RIGHT;
4807 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4808 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4809 ok (result == 0,
4810 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4811
4812 /* EM_STREAM */
4813 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4814 es.dwCookie = (DWORD_PTR)&streamText;
4815 es.dwError = 0;
4816 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4817 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4818 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4819 ok (result != 0,
4820 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4821
4822 DestroyWindow(hwndRichEdit);
4823 }
4824
4825 struct exsetsel_s {
4826 LONG min;
4827 LONG max;
4828 LRESULT expected_retval;
4829 int expected_getsel_start;
4830 int expected_getsel_end;
4831 BOOL todo;
4832 };
4833
4834 static const struct exsetsel_s exsetsel_tests[] = {
4835 /* sanity tests */
4836 {5, 10, 10, 5, 10 },
4837 {15, 17, 17, 15, 17 },
4838 /* test cpMax > strlen() */
4839 {0, 100, 18, 0, 18 },
4840 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4841 {-1, 1, 17, 17, 17 },
4842 /* test cpMin == cpMax */
4843 {5, 5, 5, 5, 5 },
4844 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4845 {-1, 0, 5, 5, 5 },
4846 {-1, 17, 5, 5, 5 },
4847 {-1, 18, 5, 5, 5 },
4848 /* test cpMin < 0 && cpMax < 0 */
4849 {-1, -1, 17, 17, 17 },
4850 {-4, -5, 17, 17, 17 },
4851 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4852 {0, -1, 18, 0, 18 },
4853 {17, -5, 18, 17, 18 },
4854 {18, -3, 17, 17, 17 },
4855 /* test if cpMin > cpMax */
4856 {15, 19, 18, 15, 18 },
4857 {19, 15, 18, 15, 18 },
4858 /* cpMin == strlen() && cpMax > cpMin */
4859 {17, 18, 18, 17, 18 },
4860 {17, 50, 18, 17, 18 },
4861 };
4862
4863 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4864 CHARRANGE cr;
4865 LRESULT result;
4866 int start, end;
4867
4868 cr.cpMin = setsel->min;
4869 cr.cpMax = setsel->max;
4870 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4871
4872 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4873
4874 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4875
4876 todo_wine_if (setsel->todo)
4877 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4878 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4879 }
4880
4881 static void test_EM_EXSETSEL(void)
4882 {
4883 HWND hwndRichEdit = new_richedit(NULL);
4884 int i;
4885 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4886
4887 /* sending some text to the window */
4888 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4889 /* 01234567890123456*/
4890 /* 10 */
4891
4892 for (i = 0; i < num_tests; i++) {
4893 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4894 }
4895
4896 if (!is_lang_japanese)
4897 skip("Skip multibyte character tests on non-Japanese platform\n");
4898 else
4899 {
4900 CHARRANGE cr;
4901 char bufA[MAX_BUF_LEN] = {0};
4902 LRESULT result;
4903
4904 /* Test with multibyte character */
4905 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4906 /* 012345 6 78901 */
4907 cr.cpMin = 4, cr.cpMax = 8;
4908 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4909 ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4910 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4911 ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4912 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4913 ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4914 ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4915 }
4916
4917 DestroyWindow(hwndRichEdit);
4918 }
4919
4920 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4921 LRESULT result;
4922 int start, end;
4923
4924 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4925
4926 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4927
4928 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4929
4930 todo_wine_if (setsel->todo)
4931 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4932 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4933 }
4934
4935 static void test_EM_SETSEL(void)
4936 {
4937 char buffA[32] = {0};
4938 HWND hwndRichEdit = new_richedit(NULL);
4939 int i;
4940 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4941
4942 /* sending some text to the window */
4943 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4944 /* 01234567890123456*/
4945 /* 10 */
4946
4947 for (i = 0; i < num_tests; i++) {
4948 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4949 }
4950
4951 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
4952 buffA[0] = 123;
4953 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
4954 ok(buffA[0] == 0, "selection text %s\n", buffA);
4955
4956 if (!is_lang_japanese)
4957 skip("Skip multibyte character tests on non-Japanese platform\n");
4958 else
4959 {
4960 int sel_start, sel_end;
4961 LRESULT result;
4962
4963 /* Test with multibyte character */
4964 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4965 /* 012345 6 78901 */
4966 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
4967 ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
4968 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
4969 ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4970 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4971 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
4972 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
4973 }
4974
4975 DestroyWindow(hwndRichEdit);
4976 }
4977
4978 static void test_EM_REPLACESEL(int redraw)
4979 {
4980 HWND hwndRichEdit = new_richedit(NULL);
4981 char buffer[1024] = {0};
4982 int r;
4983 GETTEXTEX getText;
4984 CHARRANGE cr;
4985 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
4986 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
4987
4988 /* sending some text to the window */
4989 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4990 /* 01234567890123456*/
4991 /* 10 */
4992
4993 /* FIXME add more tests */
4994 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4995 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4996 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4997 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4998 r = strcmp(buffer, "testing");
4999 ok(0 == r, "expected %d, got %d\n", 0, r);
5000
5001 DestroyWindow(hwndRichEdit);
5002
5003 hwndRichEdit = new_richedit(NULL);
5004
5005 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5006 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5007
5008 /* Test behavior with carriage returns and newlines */
5009 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5010 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5011 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5012 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5013 r = strcmp(buffer, "RichEdit1");
5014 ok(0 == r, "expected %d, got %d\n", 0, r);
5015 getText.cb = 1024;
5016 getText.codepage = CP_ACP;
5017 getText.flags = GT_DEFAULT;
5018 getText.lpDefaultChar = NULL;
5019 getText.lpUsedDefChar = NULL;
5020 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5021 ok(strcmp(buffer, "RichEdit1") == 0,
5022 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5023
5024 /* Test number of lines reported after EM_REPLACESEL */
5025 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5026 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5027
5028 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5029 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5030 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5031 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5032 r = strcmp(buffer, "RichEdit1\r\n");
5033 ok(0 == r, "expected %d, got %d\n", 0, r);
5034 getText.cb = 1024;
5035 getText.codepage = CP_ACP;
5036 getText.flags = GT_DEFAULT;
5037 getText.lpDefaultChar = NULL;
5038 getText.lpUsedDefChar = NULL;
5039 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5040 ok(strcmp(buffer, "RichEdit1\r") == 0,
5041 "EM_GETTEXTEX returned incorrect string\n");
5042
5043 /* Test number of lines reported after EM_REPLACESEL */
5044 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5045 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5046
5047 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5048 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5049 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5050
5051 /* Test number of lines reported after EM_REPLACESEL */
5052 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5053 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5054
5055 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5056 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5057 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5058 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5059
5060 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5061 r = strcmp(buffer, "RichEdit1\r\n");
5062 ok(0 == r, "expected %d, got %d\n", 0, r);
5063 getText.cb = 1024;
5064 getText.codepage = CP_ACP;
5065 getText.flags = GT_DEFAULT;
5066 getText.lpDefaultChar = NULL;
5067 getText.lpUsedDefChar = NULL;
5068 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5069 ok(strcmp(buffer, "RichEdit1\r") == 0,
5070 "EM_GETTEXTEX returned incorrect string\n");
5071
5072 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5073 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5074 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5075 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5076
5077 /* The following tests show that richedit should handle the special \r\r\n
5078 sequence by turning it into a single space on insertion. However,
5079 EM_REPLACESEL on WinXP returns the number of characters in the original
5080 string.
5081 */
5082
5083 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5084 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5085 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5086 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5087 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5088 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5089 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5090
5091 /* Test the actual string */
5092 getText.cb = 1024;
5093 getText.codepage = CP_ACP;
5094 getText.flags = GT_DEFAULT;
5095 getText.lpDefaultChar = NULL;
5096 getText.lpUsedDefChar = NULL;
5097 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5098 ok(strcmp(buffer, "\r\r") == 0,
5099 "EM_GETTEXTEX returned incorrect string\n");
5100
5101 /* Test number of lines reported after EM_REPLACESEL */
5102 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5103 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5104
5105 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5106 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5107 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5108 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5109 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5110 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
5111 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
5112
5113 /* Test the actual string */
5114 getText.cb = 1024;
5115 getText.codepage = CP_ACP;
5116 getText.flags = GT_DEFAULT;
5117 getText.lpDefaultChar = NULL;
5118 getText.lpUsedDefChar = NULL;
5119 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5120 ok(strcmp(buffer, " ") == 0,
5121 "EM_GETTEXTEX returned incorrect string\n");
5122
5123 /* Test number of lines reported after EM_REPLACESEL */
5124 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5125 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5126
5127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5128 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5129 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5130 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5131 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5132 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5133 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5134
5135 /* Test the actual string */
5136 getText.cb = 1024;
5137 getText.codepage = CP_ACP;
5138 getText.flags = GT_DEFAULT;
5139 getText.lpDefaultChar = NULL;
5140 getText.lpUsedDefChar = NULL;
5141 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5142 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5143 "EM_GETTEXTEX returned incorrect string\n");
5144
5145 /* Test number of lines reported after EM_REPLACESEL */
5146 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5147 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5148
5149 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5150 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5151 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5152 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5153 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5154 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5155 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5156
5157 /* Test the actual string */
5158 getText.cb = 1024;
5159 getText.codepage = CP_ACP;
5160 getText.flags = GT_DEFAULT;
5161 getText.lpDefaultChar = NULL;
5162 getText.lpUsedDefChar = NULL;
5163 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5164 ok(strcmp(buffer, " \r") == 0,
5165 "EM_GETTEXTEX returned incorrect string\n");
5166
5167 /* Test number of lines reported after EM_REPLACESEL */
5168 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5169 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5170
5171 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5172 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5173 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5174 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5175 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5176 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
5177 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
5178
5179 /* Test the actual string */
5180 getText.cb = 1024;
5181 getText.codepage = CP_ACP;
5182 getText.flags = GT_DEFAULT;
5183 getText.lpDefaultChar = NULL;
5184 getText.lpUsedDefChar = NULL;
5185 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5186 ok(strcmp(buffer, " \r\r") == 0,
5187 "EM_GETTEXTEX returned incorrect string\n");
5188
5189 /* Test number of lines reported after EM_REPLACESEL */
5190 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5191 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5192
5193 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5194 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5195 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5196 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5197 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5198 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
5199 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
5200
5201 /* Test the actual string */
5202 getText.cb = 1024;
5203 getText.codepage = CP_ACP;
5204 getText.flags = GT_DEFAULT;
5205 getText.lpDefaultChar = NULL;
5206 getText.lpUsedDefChar = NULL;
5207 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5208 ok(strcmp(buffer, "\rX\r\r\r") == 0,
5209 "EM_GETTEXTEX returned incorrect string\n");
5210
5211 /* Test number of lines reported after EM_REPLACESEL */
5212 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5213 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5214
5215 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5216 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5217 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5218 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5219 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5220 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5221 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5222
5223 /* Test the actual string */
5224 getText.cb = 1024;
5225 getText.codepage = CP_ACP;
5226 getText.flags = GT_DEFAULT;
5227 getText.lpDefaultChar = NULL;
5228 getText.lpUsedDefChar = NULL;
5229 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5230 ok(strcmp(buffer, "\r\r") == 0,
5231 "EM_GETTEXTEX returned incorrect string\n");
5232
5233 /* Test number of lines reported after EM_REPLACESEL */
5234 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5235 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5236
5237 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5238 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5239 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5240 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5241 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5242 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5243 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5244
5245 /* Test the actual string */
5246 getText.cb = 1024;
5247 getText.codepage = CP_ACP;
5248 getText.flags = GT_DEFAULT;
5249 getText.lpDefaultChar = NULL;
5250 getText.lpUsedDefChar = NULL;
5251 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5252 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5253 "EM_GETTEXTEX returned incorrect string\n");
5254
5255 /* Test number of lines reported after EM_REPLACESEL */
5256 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5257 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5258
5259 /* Test with multibyte character */
5260 if (!is_lang_japanese)
5261 skip("Skip multibyte character tests on non-Japanese platform\n");
5262 else
5263 {
5264 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5265 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5266 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5267 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5268 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5269 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5270 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5271 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5272 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5273 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5274
5275 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5276 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5277 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5278 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5279 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5280 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5281 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5282 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5283 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5284 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5285 }
5286
5287 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5288 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5289 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5290 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5291 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5292 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5293 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5294 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5295 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5296
5297 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5298 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5299 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5300 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5301 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5302 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5303 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5304 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5305 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5306
5307 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5308 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5309 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5310 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5311 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5312 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5313 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5314 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5315 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5316 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5317
5318 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5319 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5320 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5321 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5322 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5323 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5324 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5325 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5326 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5327 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5328
5329 if (!redraw)
5330 /* This is needed to avoid interfering with keybd_event calls
5331 * on other tests that simulate keyboard events. */
5332 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5333
5334 DestroyWindow(hwndRichEdit);
5335 }
5336
5337 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5338 * to test the state of the modifiers (Ctrl/Alt/Shift).
5339 *
5340 * Therefore Ctrl-<key> keystrokes need to be simulated with
5341 * keybd_event or by using SetKeyboardState to set the modifiers
5342 * and SendMessage to simulate the keystrokes.
5343 */
5344 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5345 {
5346 LRESULT result;
5347 hold_key(VK_CONTROL);
5348 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5349 release_key(VK_CONTROL);
5350 return result;
5351 }
5352
5353 static void test_WM_PASTE(void)
5354 {
5355 int result;
5356 char buffer[1024] = {0};
5357 const char* text1 = "testing paste\r";
5358 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5359 const char* text1_after = "testing paste\r\n";
5360 const char* text2 = "testing paste\r\rtesting paste";
5361 const char* text2_after = "testing paste\r\n\r\ntesting paste";
5362 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5363 HWND hwndRichEdit = new_richedit(NULL);
5364
5365 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5366 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5367
5368 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5369 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5370 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5371 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5372 /* Pasted text should be visible at this step */
5373 result = strcmp(text1_step1, buffer);
5374 ok(result == 0,
5375 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5376
5377 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5378 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5379 /* Text should be the same as before (except for \r -> \r\n conversion) */
5380 result = strcmp(text1_after, buffer);
5381 ok(result == 0,
5382 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5383
5384 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5385 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5386 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5387 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5388 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5389 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5390 /* Pasted text should be visible at this step */
5391 result = strcmp(text3, buffer);
5392 ok(result == 0,
5393 "test paste: strcmp = %i\n", result);
5394 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5395 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5396 /* Text should be the same as before (except for \r -> \r\n conversion) */
5397 result = strcmp(text2_after, buffer);
5398 ok(result == 0,
5399 "test paste: strcmp = %i\n", result);
5400 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5401 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5402 /* Text should revert to post-paste state */
5403 result = strcmp(buffer,text3);
5404 ok(result == 0,
5405 "test paste: strcmp = %i\n", result);
5406
5407 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5408 /* Send WM_CHAR to simulate Ctrl-V */
5409 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5410 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5411 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5412 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5413 result = strcmp(buffer,"");
5414 ok(result == 0,
5415 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5416
5417 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5418 * with SetKeyboard state. */
5419
5420 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5421 /* Simulates paste (Ctrl-V) */
5422 hold_key(VK_CONTROL);
5423 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5424 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5425 release_key(VK_CONTROL);
5426 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5427 result = strcmp(buffer,"paste");
5428 ok(result == 0,
5429 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5430
5431 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5432 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5433 /* Simulates copy (Ctrl-C) */
5434 hold_key(VK_CONTROL);
5435 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5436 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5437 release_key(VK_CONTROL);
5438 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5439 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5440 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5441 result = strcmp(buffer,"testing");
5442 ok(result == 0,
5443 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5444
5445 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5446 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5447 /* Simulates select all (Ctrl-A) */
5448 hold_key(VK_CONTROL);
5449 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5450 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5451 /* Simulates select cut (Ctrl-X) */
5452 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5453 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5454 release_key(VK_CONTROL);
5455 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5456 result = strcmp(buffer,"");
5457 ok(result == 0,
5458 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5459 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5460 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5461 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5462 result = strcmp(buffer,"cut\r\n");
5463 ok(result == 0,
5464 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5465 /* Simulates undo (Ctrl-Z) */
5466 hold_key(VK_CONTROL);
5467 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5468 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5469 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5470 result = strcmp(buffer,"");
5471 ok(result == 0,
5472 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5473 /* Simulates redo (Ctrl-Y) */
5474 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5475 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5476 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5477 result = strcmp(buffer,"cut\r\n");
5478 ok(result == 0,
5479 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5480 release_key(VK_CONTROL);
5481
5482 DestroyWindow(hwndRichEdit);
5483 }
5484
5485 static void test_EM_FORMATRANGE(void)
5486 {
5487 int r, i, tpp_x, tpp_y;
5488 HDC hdc;
5489 HWND hwndRichEdit = new_richedit(NULL);
5490 FORMATRANGE fr;
5491 BOOL skip_non_english;
5492 static const struct {
5493 const char *string; /* The string */
5494 int first; /* First 'pagebreak', 0 for don't care */
5495 int second; /* Second 'pagebreak', 0 for don't care */
5496 } fmtstrings[] = {
5497 {"WINE wine", 0, 0},
5498 {"WINE wineWine", 0, 0},
5499 {"WINE\r\nwine\r\nwine", 5, 10},
5500 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5501 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5502 };
5503
5504 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5505 if (skip_non_english)
5506 skip("Skipping some tests on non-English platform\n");
5507
5508 hdc = GetDC(hwndRichEdit);
5509 ok(hdc != NULL, "Could not get HDC\n");
5510
5511 /* Calculate the twips per pixel */
5512 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5513 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5514
5515 /* Test the simple case where all the text fits in the page rect. */
5516 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5517 fr.hdc = fr.hdcTarget = hdc;
5518 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5519 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5520 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5521 fr.chrg.cpMin = 0;
5522 fr.chrg.cpMax = -1;
5523 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5524 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5525
5526 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5527 fr.rc.bottom = fr.rcPage.bottom;
5528 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5529 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5530
5531 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5532
5533 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5534 {
5535 GETTEXTLENGTHEX gtl;
5536 SIZE stringsize;
5537 int len;
5538
5539 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5540
5541 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5542 gtl.codepage = CP_ACP;
5543 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5544
5545 /* Get some size information for the string */
5546 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5547
5548 /* Define the box to be half the width needed and a bit larger than the height.
5549 * Changes to the width means we have at least 2 pages. Changes to the height
5550 * is done so we can check the changing of fr.rc.bottom.
5551 */
5552 fr.hdc = fr.hdcTarget = hdc;
5553 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5554 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5555 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5556
5557 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5558 todo_wine {
5559 ok(r == len, "Expected %d, got %d\n", len, r);
5560 }
5561
5562 /* We know that the page can't hold the full string. See how many characters
5563 * are on the first one
5564 */
5565 fr.chrg.cpMin = 0;
5566 fr.chrg.cpMax = -1;
5567 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5568 todo_wine {
5569 if (! skip_non_english)
5570 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5571 }
5572 if (fmtstrings[i].first)
5573 todo_wine {
5574 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5575 }
5576 else
5577 ok(r < len, "Expected < %d, got %d\n", len, r);
5578
5579 /* Do another page */
5580 fr.chrg.cpMin = r;
5581 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5582 if (fmtstrings[i].second)
5583 todo_wine {
5584 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5585 }
5586 else if (! skip_non_english)
5587 ok (r < len, "Expected < %d, got %d\n", len, r);
5588
5589 /* There is at least on more page, but we don't care */
5590
5591 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5592 todo_wine {
5593 ok(r == len, "Expected %d, got %d\n", len, r);
5594 }
5595 }
5596
5597 ReleaseDC(NULL, hdc);
5598 DestroyWindow(hwndRichEdit);
5599 }
5600
5601 static int nCallbackCount = 0;
5602
5603 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5604 LONG cb, LONG* pcb)
5605 {
5606 const char text[] = {'t','e','s','t'};
5607
5608 if (sizeof(text) <= cb)
5609 {
5610 if ((int)dwCookie != nCallbackCount)
5611 {
5612 *pcb = 0;
5613 return 0;
5614 }
5615
5616 memcpy (pbBuff, text, sizeof(text));
5617 *pcb = sizeof(text);
5618
5619 nCallbackCount++;
5620
5621 return 0;
5622 }
5623 else
5624 return 1; /* indicates callback failed */
5625 }
5626
5627 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5628 LPBYTE pbBuff,
5629 LONG cb,
5630 LONG *pcb)
5631 {
5632 const char** str = (const char**)dwCookie;
5633 int size = strlen(*str);
5634 *pcb = cb;
5635 if (*pcb > size) {
5636 *pcb = size;
5637 }
5638 if (*pcb > 0) {
5639 memcpy(pbBuff, *str, *pcb);
5640 *str += *pcb;
5641 }
5642 return 0;
5643 }
5644
5645 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5646 LPBYTE pbBuff,
5647 LONG cb,
5648 LONG *pcb)
5649 {
5650 DWORD *phase = (DWORD *)dwCookie;
5651
5652 if(*phase == 0){
5653 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5654 *pcb = sizeof(first) - 1;
5655 memcpy(pbBuff, first, *pcb);
5656 }else if(*phase == 1){
5657 static const char second[] = "\x8f\xc3\x8b";
5658 *pcb = sizeof(second) - 1;
5659 memcpy(pbBuff, second, *pcb);
5660 }else
5661 *pcb = 0;
5662
5663 ++*phase;
5664
5665 return 0;
5666 }
5667
5668 struct StringWithLength {
5669 int length;
5670 char *buffer;
5671 };
5672
5673 /* This callback is used to handled the null characters in a string. */
5674 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5675 LPBYTE pbBuff,
5676 LONG cb,
5677 LONG *pcb)
5678 {
5679 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5680 int size = str->length;
5681 *pcb = cb;
5682 if (*pcb > size) {
5683 *pcb = size;
5684 }
5685 if (*pcb > 0) {
5686 memcpy(pbBuff, str->buffer, *pcb);
5687 str->buffer += *pcb;
5688 str->length -= *pcb;
5689 }
5690 return 0;
5691 }
5692
5693 static void test_EM_STREAMIN(void)
5694 {
5695 HWND hwndRichEdit = new_richedit(NULL);
5696 DWORD phase;
5697 LRESULT result;
5698 EDITSTREAM es;
5699 char buffer[1024] = {0}, tmp[16];
5700 CHARRANGE range;
5701 PARAFORMAT2 fmt;
5702
5703 const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5704 const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5705 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5706 const char * ptr;
5707
5708 const char * streamText1 =
5709 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5710 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5711 "}\r\n";
5712
5713 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5714 const char * streamText2 =
5715 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5716 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5717 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5718 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5719 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5720 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5721 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5722
5723 const char * streamText3 = "RichEdit1";
5724
5725 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5726
5727 const char * streamText4 =
5728 "This text just needs to be long enough to cause run to be split onto "
5729 "two separate lines and make sure the null terminating character is "
5730 "handled properly.\0";
5731
5732 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5733
5734 int length4 = strlen(streamText4) + 1;
5735 struct StringWithLength cookieForStream4 = {
5736 length4,
5737 (char *)streamText4,
5738 };
5739
5740 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5741 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5742 struct StringWithLength cookieForStream5 = {
5743 sizeof(streamText5),
5744 (char *)streamText5,
5745 };
5746
5747 /* Minimal test without \par at the end */
5748 es.dwCookie = (DWORD_PTR)&streamText0;
5749 es.dwError = 0;
5750 es.pfnCallback = test_EM_STREAMIN_esCallback;
5751 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5752 ok(result == 12, "got %ld, expected %d\n", result, 12);
5753
5754 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5755 ok (result == 12,
5756 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5757 result = strcmp (buffer,"TestSomeText");
5758 ok (result == 0,
5759 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5760 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5761 /* Show that para fmts are ignored */
5762 range.cpMin = 2;
5763 range.cpMax = 2;
5764 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5765 memset(&fmt, 0xcc, sizeof(fmt));
5766 fmt.cbSize = sizeof(fmt);
5767 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5768 ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5769 ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5770 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5771 ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5772
5773 /* Native richedit 2.0 ignores last \par */
5774 ptr = streamText0a;
5775 es.dwCookie = (DWORD_PTR)&ptr;
5776 es.dwError = 0;
5777 es.pfnCallback = test_EM_STREAMIN_esCallback;
5778 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5779 ok(result == 12, "got %ld, expected %d\n", result, 12);
5780
5781 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5782 ok (result == 12,
5783 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5784 result = strcmp (buffer,"TestSomeText");
5785 ok (result == 0,
5786 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5787 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5788 /* This time para fmts are processed */
5789 range.cpMin = 2;
5790 range.cpMax = 2;
5791 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5792 memset(&fmt, 0xcc, sizeof(fmt));
5793 fmt.cbSize = sizeof(fmt);
5794 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5795 ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5796 ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5797 ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5798 ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5799
5800 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5801 es.dwCookie = (DWORD_PTR)&streamText0b;
5802 es.dwError = 0;
5803 es.pfnCallback = test_EM_STREAMIN_esCallback;
5804 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5805 ok(result == 13, "got %ld, expected %d\n", result, 13);
5806
5807 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5808 ok (result == 14,
5809 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5810 result = strcmp (buffer,"TestSomeText\r\n");
5811 ok (result == 0,
5812 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5813 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5814
5815 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5816 ptr = streamText0a;
5817 es.dwCookie = (DWORD_PTR)&ptr;
5818 es.dwError = 0;
5819 es.pfnCallback = test_EM_STREAMIN_esCallback;
5820 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5821 ok(result == 12, "got %ld, expected %d\n", result, 12);
5822
5823 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5824 ok (result == 12,
5825 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5826 result = strcmp (buffer,"TestSomeText");
5827 ok (result == 0,
5828 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5829 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5830
5831 range.cpMin = 0;
5832 range.cpMax = -1;
5833 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5834 ok (result == 13, "got %ld\n", result);
5835
5836 ptr = streamText0a;
5837 es.dwCookie = (DWORD_PTR)&ptr;
5838 es.dwError = 0;
5839 es.pfnCallback = test_EM_STREAMIN_esCallback;
5840
5841 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5842 ok(result == 13, "got %ld, expected 13\n", result);
5843
5844 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5845 ok (result == 14,
5846 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5847 result = strcmp (buffer,"TestSomeText\r\n");
5848 ok (result == 0,
5849 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5850 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5851
5852 es.dwCookie = (DWORD_PTR)&streamText1;
5853 es.dwError = 0;
5854 es.pfnCallback = test_EM_STREAMIN_esCallback;
5855 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5856 ok(result == 12, "got %ld, expected %d\n", result, 12);
5857
5858 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5859 ok (result == 12,
5860 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5861 result = strcmp (buffer,"TestSomeText");
5862 ok (result == 0,
5863 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5864 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5865
5866 es.dwCookie = (DWORD_PTR)&streamText2;
5867 es.dwError = 0;
5868 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5869 ok(result == 0, "got %ld, expected %d\n", result, 0);
5870
5871 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5872 ok (result == 0,
5873 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5874 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5875 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5876
5877 es.dwCookie = (DWORD_PTR)&streamText3;
5878 es.dwError = 0;
5879 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5880 ok(result == 0, "got %ld, expected %d\n", result, 0);
5881
5882 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5883 ok (result == 0,
5884 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5885 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5886 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5887
5888 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5889 es.dwError = 0;
5890 es.pfnCallback = test_EM_STREAMIN_esCallback;
5891 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5892 ok(result == 18, "got %ld, expected %d\n", result, 18);
5893
5894 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5895 ok(result == 15,
5896 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5897 result = strcmp (buffer,"TestUTF8WithBOM");
5898 ok(result == 0,
5899 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5900 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5901
5902 phase = 0;
5903 es.dwCookie = (DWORD_PTR)&phase;
5904 es.dwError = 0;
5905 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5906 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5907 ok(result == 8, "got %ld\n", result);
5908
5909 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5910
5911 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5912 ok(result == 3,
5913 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5914 result = memcmp (buffer, tmp, 3);
5915 ok(result == 0,
5916 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5917 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5918
5919 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5920 es.dwError = 0;
5921 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5922 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5923 ok(result == length4, "got %ld, expected %d\n", result, length4);
5924
5925 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5926 ok (result == length4,
5927 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5928 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5929
5930 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5931 es.dwError = 0;
5932 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5933 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5934 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5935
5936 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5937 ok (result == length5,
5938 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5939 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5940
5941 DestroyWindow(hwndRichEdit);
5942 }
5943
5944 static void test_EM_StreamIn_Undo(void)
5945 {
5946 /* The purpose of this test is to determine when a EM_StreamIn should be
5947 * undoable. This is important because WM_PASTE currently uses StreamIn and
5948 * pasting should always be undoable but streaming isn't always.
5949 *
5950 * cases to test:
5951 * StreamIn plain text without SFF_SELECTION.
5952 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5953 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5954 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5955 * Feel free to add tests for other text modes or StreamIn things.
5956 */
5957
5958
5959 HWND hwndRichEdit = new_richedit(NULL);
5960 LRESULT result;
5961 EDITSTREAM es;
5962 char buffer[1024] = {0};
5963 const char randomtext[] = "Some text";
5964
5965 es.pfnCallback = EditStreamCallback;
5966
5967 /* StreamIn, no SFF_SELECTION */
5968 es.dwCookie = nCallbackCount;
5969 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5970 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5971 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5972 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5973 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5974 result = strcmp (buffer,"test");
5975 ok (result == 0,
5976 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5977
5978 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5979 ok (result == FALSE,
5980 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5981
5982 /* StreamIn, SFF_SELECTION, but nothing selected */
5983 es.dwCookie = nCallbackCount;
5984 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5985 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5986 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5987 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5988 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5989 result = strcmp (buffer,"testSome text");
5990 ok (result == 0,
5991 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5992
5993 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5994 ok (result == TRUE,
5995 "EM_STREAMIN with SFF_SELECTION but no selection set "
5996 "should create an undo\n");
5997
5998 /* StreamIn, SFF_SELECTION, with a selection */
5999 es.dwCookie = nCallbackCount;
6000 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6001 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6002 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6003 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6004 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6005 result = strcmp (buffer,"Sometesttext");
6006 ok (result == 0,
6007 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6008
6009 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6010 ok (result == TRUE,
6011 "EM_STREAMIN with SFF_SELECTION and selection set "
6012 "should create an undo\n");
6013
6014 DestroyWindow(hwndRichEdit);
6015 }
6016
6017 static BOOL is_em_settextex_supported(HWND hwnd)
6018 {
6019 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6020 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6021 }
6022
6023 static void test_unicode_conversions(void)
6024 {
6025 static const WCHAR tW[] = {'t',0};
6026 static const WCHAR teW[] = {'t','e',0};
6027 static const WCHAR textW[] = {'t','e','s','t',0};
6028 static const char textA[] = "test";
6029 char bufA[64];
6030 WCHAR bufW[64];
6031 HWND hwnd;
6032 int em_settextex_supported, ret;
6033
6034 #define set_textA(hwnd, wm_set_text, txt) \
6035 do { \
6036 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6037 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6038 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6039 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6040 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
6041 } while(0)
6042 #define expect_textA(hwnd, wm_get_text, txt) \
6043 do { \
6044 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6045 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6046 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6047 memset(bufA, 0xAA, sizeof(bufA)); \
6048 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6049 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
6050 ret = lstrcmpA(bufA, txt); \
6051 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6052 } while(0)
6053
6054 #define set_textW(hwnd, wm_set_text, txt) \
6055 do { \
6056 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6057 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6058 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6059 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6060 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
6061 } while(0)
6062 #define expect_textW(hwnd, wm_get_text, txt) \
6063 do { \
6064 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6065 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6066 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6067 memset(bufW, 0xAA, sizeof(bufW)); \
6068 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6069 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
6070 ret = lstrcmpW(bufW, txt); \
6071 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6072 } while(0)
6073 #define expect_empty(hwnd, wm_get_text) \
6074 do { \
6075 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6076 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6077 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6078 memset(bufA, 0xAA, sizeof(bufA)); \
6079 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6080 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6081 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6082 } while(0)
6083
6084 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6085 0, 0, 200, 60, 0, 0, 0, 0);
6086 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6087
6088 ret = IsWindowUnicode(hwnd);
6089 ok(ret, "RichEdit20W should be unicode under NT\n");
6090
6091 /* EM_SETTEXTEX is supported starting from version 3.0 */
6092 em_settextex_supported = is_em_settextex_supported(hwnd);
6093 trace("EM_SETTEXTEX is %ssupported on this platform\n",
6094 em_settextex_supported ? "" : "NOT ");
6095
6096 expect_empty(hwnd, WM_GETTEXT);
6097 expect_empty(hwnd, EM_GETTEXTEX);
6098
6099 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6100 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6101 expect_textA(hwnd, WM_GETTEXT, "t");
6102 expect_textA(hwnd, EM_GETTEXTEX, "t");
6103 expect_textW(hwnd, EM_GETTEXTEX, tW);
6104
6105 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6106 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6107 expect_textA(hwnd, WM_GETTEXT, "te");
6108 expect_textA(hwnd, EM_GETTEXTEX, "te");
6109 expect_textW(hwnd, EM_GETTEXTEX, teW);
6110
6111 set_textA(hwnd, WM_SETTEXT, NULL);
6112 expect_empty(hwnd, WM_GETTEXT);
6113 expect_empty(hwnd, EM_GETTEXTEX);
6114
6115 set_textA(hwnd, WM_SETTEXT, textA);
6116 expect_textA(hwnd, WM_GETTEXT, textA);
6117 expect_textA(hwnd, EM_GETTEXTEX, textA);
6118 expect_textW(hwnd, EM_GETTEXTEX, textW);
6119
6120 if (em_settextex_supported)
6121 {
6122 set_textA(hwnd, EM_SETTEXTEX, textA);
6123 expect_textA(hwnd, WM_GETTEXT, textA);
6124 expect_textA(hwnd, EM_GETTEXTEX, textA);
6125 expect_textW(hwnd, EM_GETTEXTEX, textW);
6126 }
6127
6128 set_textW(hwnd, WM_SETTEXT, textW);
6129 expect_textW(hwnd, WM_GETTEXT, textW);
6130 expect_textA(hwnd, WM_GETTEXT, textA);
6131 expect_textW(hwnd, EM_GETTEXTEX, textW);
6132 expect_textA(hwnd, EM_GETTEXTEX, textA);
6133
6134 if (em_settextex_supported)
6135 {
6136 set_textW(hwnd, EM_SETTEXTEX, textW);
6137 expect_textW(hwnd, WM_GETTEXT, textW);
6138 expect_textA(hwnd, WM_GETTEXT, textA);
6139 expect_textW(hwnd, EM_GETTEXTEX, textW);
6140 expect_textA(hwnd, EM_GETTEXTEX, textA);
6141 }
6142 DestroyWindow(hwnd);
6143
6144 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6145 0, 0, 200, 60, 0, 0, 0, 0);
6146 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6147
6148 ret = IsWindowUnicode(hwnd);
6149 ok(!ret, "RichEdit20A should NOT be unicode\n");
6150
6151 set_textA(hwnd, WM_SETTEXT, textA);
6152 expect_textA(hwnd, WM_GETTEXT, textA);
6153 expect_textA(hwnd, EM_GETTEXTEX, textA);
6154 expect_textW(hwnd, EM_GETTEXTEX, textW);
6155
6156 if (em_settextex_supported)
6157 {
6158 set_textA(hwnd, EM_SETTEXTEX, textA);
6159 expect_textA(hwnd, WM_GETTEXT, textA);
6160 expect_textA(hwnd, EM_GETTEXTEX, textA);
6161 expect_textW(hwnd, EM_GETTEXTEX, textW);
6162 }
6163
6164 set_textW(hwnd, WM_SETTEXT, textW);
6165 expect_textW(hwnd, WM_GETTEXT, textW);
6166 expect_textA(hwnd, WM_GETTEXT, textA);
6167 expect_textW(hwnd, EM_GETTEXTEX, textW);
6168 expect_textA(hwnd, EM_GETTEXTEX, textA);
6169
6170 if (em_settextex_supported)
6171 {
6172 set_textW(hwnd, EM_SETTEXTEX, textW);
6173 expect_textW(hwnd, WM_GETTEXT, textW);
6174 expect_textA(hwnd, WM_GETTEXT, textA);
6175 expect_textW(hwnd, EM_GETTEXTEX, textW);
6176 expect_textA(hwnd, EM_GETTEXTEX, textA);
6177 }
6178 DestroyWindow(hwnd);
6179 }
6180
6181 static void test_WM_CHAR(void)
6182 {
6183 HWND hwnd;
6184 int ret;
6185 const char * char_list = "abc\rabc\r";
6186 const char * expected_content_single = "abcabc";
6187 const char * expected_content_multi = "abc\r\nabc\r\n";
6188 char buffer[64] = {0};
6189 const char * p;
6190
6191 /* single-line control must IGNORE carriage returns */
6192 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6193 0, 0, 200, 60, 0, 0, 0, 0);
6194 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6195
6196 p = char_list;
6197 while (*p != '\0') {
6198 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6199 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6200 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6201 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6202 p++;
6203 }
6204
6205 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6206 ret = strcmp(buffer, expected_content_single);
6207 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6208
6209 DestroyWindow(hwnd);
6210
6211 /* multi-line control inserts CR normally */
6212 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6213 0, 0, 200, 60, 0, 0, 0, 0);
6214 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6215
6216 p = char_list;
6217 while (*p != '\0') {
6218 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6219 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6220 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6221 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6222 p++;
6223 }
6224
6225 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6226 ret = strcmp(buffer, expected_content_multi);
6227 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6228
6229 DestroyWindow(hwnd);
6230 }
6231
6232 static void test_EM_GETTEXTLENGTHEX(void)
6233 {
6234 HWND hwnd;
6235 GETTEXTLENGTHEX gtl;
6236 int ret;
6237 const char * base_string = "base string";
6238 const char * test_string = "a\nb\n\n\r\n";
6239 const char * test_string_after = "a";
6240 const char * test_string_2 = "a\rtest\rstring";
6241 char buffer[64] = {0};
6242
6243 /* single line */
6244 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6245 0, 0, 200, 60, 0, 0, 0, 0);
6246 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6247
6248 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6249 gtl.codepage = CP_ACP;
6250 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6251 ok(ret == 0, "ret %d\n",ret);
6252
6253 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6254 gtl.codepage = CP_ACP;
6255 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6256 ok(ret == 0, "ret %d\n",ret);
6257
6258 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6259
6260 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6261 gtl.codepage = CP_ACP;
6262 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6263 ok(ret == strlen(base_string), "ret %d\n",ret);
6264
6265 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6266 gtl.codepage = CP_ACP;
6267 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6268 ok(ret == strlen(base_string), "ret %d\n",ret);
6269
6270 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6271
6272 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6273 gtl.codepage = CP_ACP;
6274 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6275 ok(ret == 1, "ret %d\n",ret);
6276
6277 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6278 gtl.codepage = CP_ACP;
6279 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6280 ok(ret == 1, "ret %d\n",ret);
6281
6282 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6283 ret = strcmp(buffer, test_string_after);
6284 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6285
6286 DestroyWindow(hwnd);
6287
6288 /* multi line */
6289 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6290 0, 0, 200, 60, 0, 0, 0, 0);
6291 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6292
6293 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6294 gtl.codepage = CP_ACP;
6295 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6296 ok(ret == 0, "ret %d\n",ret);
6297
6298 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6299 gtl.codepage = CP_ACP;
6300 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6301 ok(ret == 0, "ret %d\n",ret);
6302
6303 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6304
6305 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6306 gtl.codepage = CP_ACP;
6307 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6308 ok(ret == strlen(base_string), "ret %d\n",ret);
6309
6310 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6311 gtl.codepage = CP_ACP;
6312 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6313 ok(ret == strlen(base_string), "ret %d\n",ret);
6314
6315 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6316
6317 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6318 gtl.codepage = CP_ACP;
6319 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6320 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6321
6322 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6323 gtl.codepage = CP_ACP;
6324 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6325 ok(ret == strlen(test_string_2), "ret %d\n",ret);
6326
6327 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6328
6329 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6330 gtl.codepage = CP_ACP;
6331 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6332 ok(ret == 10, "ret %d\n",ret);
6333
6334 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6335 gtl.codepage = CP_ACP;
6336 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6337 ok(ret == 6, "ret %d\n",ret);
6338
6339 /* Unicode/NUMCHARS/NUMBYTES */
6340 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6341
6342 gtl.flags = GTL_DEFAULT;
6343 gtl.codepage = 1200;
6344 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6345 ok(ret == lstrlenA(test_string_2),
6346 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6347
6348 gtl.flags = GTL_NUMCHARS;
6349 gtl.codepage = 1200;
6350 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6351 ok(ret == lstrlenA(test_string_2),
6352 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6353
6354 gtl.flags = GTL_NUMBYTES;
6355 gtl.codepage = 1200;
6356 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6357 ok(ret == lstrlenA(test_string_2)*2,
6358 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6359
6360 gtl.flags = GTL_PRECISE;
6361 gtl.codepage = 1200;
6362 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6363 ok(ret == lstrlenA(test_string_2)*2,
6364 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6365
6366 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6367 gtl.codepage = 1200;
6368 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6369 ok(ret == lstrlenA(test_string_2),
6370 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6371
6372 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6373 gtl.codepage = 1200;
6374 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6375 ok(ret == E_INVALIDARG,
6376 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6377
6378 DestroyWindow(hwnd);
6379 }
6380
6381
6382 /* globals that parent and child access when checking event masks & notifications */
6383 static HWND eventMaskEditHwnd = 0;
6384 static int queriedEventMask;
6385 static int watchForEventMask = 0;
6386
6387 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6388 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6389 {
6390 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6391 {
6392 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6393 }
6394 return DefWindowProcA(hwnd, message, wParam, lParam);
6395 }
6396
6397 /* test event masks in combination with WM_COMMAND */
6398 static void test_eventMask(void)
6399 {
6400 HWND parent;
6401 int ret, style;
6402 WNDCLASSA cls;
6403 const char text[] = "foo bar\n";
6404 int eventMask;
6405
6406 /* register class to capture WM_COMMAND */
6407 cls.style = 0;
6408 cls.lpfnWndProc = ParentMsgCheckProcA;
6409 cls.cbClsExtra = 0;
6410 cls.cbWndExtra = 0;
6411 cls.hInstance = GetModuleHandleA(0);
6412 cls.hIcon = 0;
6413 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6414 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6415 cls.lpszMenuName = NULL;
6416 cls.lpszClassName = "EventMaskParentClass";
6417 if(!RegisterClassA(&cls)) assert(0);
6418
6419 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6420 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6421 ok (parent != 0, "Failed to create parent window\n");
6422
6423 eventMaskEditHwnd = new_richedit(parent);
6424 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6425
6426 eventMask = ENM_CHANGE | ENM_UPDATE;
6427 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6428 ok(ret == ENM_NONE, "wrong event mask\n");
6429 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6430 ok(ret == eventMask, "failed to set event mask\n");
6431
6432 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6433 queriedEventMask = 0; /* initialize to something other than we expect */
6434 watchForEventMask = EN_CHANGE;
6435 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6436 ok(ret == TRUE, "failed to set text\n");
6437 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6438 notification in response to WM_SETTEXT */
6439 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6440 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6441
6442 /* check to see if EN_CHANGE is sent when redraw is turned off */
6443 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6444 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6445 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6446 /* redraw is disabled by making the window invisible. */
6447 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6448 queriedEventMask = 0; /* initialize to something other than we expect */
6449 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6450 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6451 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6452 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6453 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6454
6455 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6456 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6457 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6458 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6459 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6460 watchForEventMask = EN_UPDATE;
6461 queriedEventMask = 0; /* initialize to something other than we expect */
6462 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6463 ok(queriedEventMask == 0,
6464 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6465 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6466 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6467 queriedEventMask = 0; /* initialize to something other than we expect */
6468 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6469 ok(queriedEventMask == eventMask,
6470 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6471
6472
6473 DestroyWindow(parent);
6474 }
6475
6476 static int received_WM_NOTIFY = 0;
6477 static int modify_at_WM_NOTIFY = 0;
6478 static BOOL filter_on_WM_NOTIFY = FALSE;
6479 static HWND hwndRichedit_WM_NOTIFY;
6480
6481 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6482 {
6483 if(message == WM_NOTIFY)
6484 {
6485 received_WM_NOTIFY = 1;
6486 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6487 if (filter_on_WM_NOTIFY) return TRUE;
6488 }
6489 return DefWindowProcA(hwnd, message, wParam, lParam);
6490 }
6491
6492 static void test_WM_NOTIFY(void)
6493 {
6494 HWND parent;
6495 WNDCLASSA cls;
6496 CHARFORMAT2A cf2;
6497 int sel_start, sel_end;
6498
6499 /* register class to capture WM_NOTIFY */
6500 cls.style = 0;
6501 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6502 cls.cbClsExtra = 0;
6503 cls.cbWndExtra = 0;
6504 cls.hInstance = GetModuleHandleA(0);
6505 cls.hIcon = 0;
6506 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6507 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6508 cls.lpszMenuName = NULL;
6509 cls.lpszClassName = "WM_NOTIFY_ParentClass";
6510 if(!RegisterClassA(&cls)) assert(0);
6511
6512 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6513 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6514 ok (parent != 0, "Failed to create parent window\n");
6515
6516 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6517 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6518
6519 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6520
6521 /* Notifications for selection change should only be sent when selection
6522 actually changes. EM_SETCHARFORMAT is one message that calls
6523 ME_CommitUndo, which should check whether message should be sent */
6524 received_WM_NOTIFY = 0;
6525 cf2.cbSize = sizeof(CHARFORMAT2A);
6526 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6527 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6528 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6529 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6530 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6531
6532 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6533 already at 0. */
6534 received_WM_NOTIFY = 0;
6535 modify_at_WM_NOTIFY = 0;
6536 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6537 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6538 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6539
6540 received_WM_NOTIFY = 0;
6541 modify_at_WM_NOTIFY = 0;
6542 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6543 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6544
6545 received_WM_NOTIFY = 0;
6546 modify_at_WM_NOTIFY = 0;
6547 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6548 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6549 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6550
6551 /* Test for WM_NOTIFY messages with redraw disabled. */
6552 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6553 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6554 received_WM_NOTIFY = 0;
6555 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6556 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6557 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6558
6559 /* Test filtering key events. */
6560 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6561 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6562 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6563 received_WM_NOTIFY = 0;
6564 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6565 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6566 ok(sel_start == 1 && sel_end == 1,
6567 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6568 filter_on_WM_NOTIFY = TRUE;
6569 received_WM_NOTIFY = 0;
6570 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6571 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6572 ok(sel_start == 1 && sel_end == 1,
6573 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6574
6575 /* test with owner set to NULL */
6576 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6577 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6578 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6579 ok(sel_start == 1 && sel_end == 1,
6580 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6581
6582 DestroyWindow(hwndRichedit_WM_NOTIFY);
6583 DestroyWindow(parent);
6584 }
6585
6586 static ENLINK enlink;
6587 #define CURSOR_CLIENT_X 5
6588 #define CURSOR_CLIENT_Y 5
6589 #define WP_PARENT 1
6590 #define WP_CHILD 2
6591
6592 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6593 {
6594 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6595 {
6596 enlink = *(ENLINK*)lParam;
6597 }
6598 return DefWindowProcA(hwnd, message, wParam, lParam);
6599 }
6600
6601 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6602 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6603 {
6604 ENLINK junk_enlink;
6605
6606 switch (msg)
6607 {
6608 case WM_LBUTTONDBLCLK:
6609 case WM_LBUTTONDOWN:
6610 case WM_LBUTTONUP:
6611 case WM_MOUSEHOVER:
6612 case WM_MOUSEMOVE:
6613 case WM_MOUSEWHEEL:
6614 case WM_RBUTTONDBLCLK:
6615 case WM_RBUTTONDOWN:
6616 case WM_RBUTTONUP:
6617 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6618 break;
6619 case WM_SETCURSOR:
6620 if (wParam == WP_PARENT)
6621 wParam = (WPARAM)parent;
6622 else if (wParam == WP_CHILD)
6623 wParam = (WPARAM)hwnd;
6624 break;
6625 }
6626
6627 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6628 enlink = junk_enlink;
6629
6630 SendMessageA(hwnd, msg, wParam, lParam);
6631
6632 if (notifies)
6633 {
6634 ok(enlink.nmhdr.hwndFrom == hwnd,
6635 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6636 ok(enlink.nmhdr.idFrom == 0,
6637 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6638 ok(enlink.msg == msg,
6639 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6640 if (msg == WM_SETCURSOR)
6641 {
6642 ok(enlink.wParam == 0,
6643 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6644 }
6645 else
6646 {
6647 ok(enlink.wParam == wParam,
6648 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6649 }
6650 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6651 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6652 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6653 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6654 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6655 }
6656 else
6657 {
6658 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6659 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6660 }
6661 }
6662
6663 static void test_EN_LINK(void)
6664 {
6665 HWND hwnd, parent;
6666 WNDCLASSA cls;
6667 CHARFORMAT2A cf2;
6668 POINT orig_cursor_pos;
6669 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6670 int i;
6671
6672 static const struct
6673 {
6674 UINT msg;
6675 WPARAM wParam;
6676 LPARAM lParam;
6677 BOOL notifies;
6678 }
6679 link_notify_tests[] =
6680 {
6681 /* hold down the left button and try some messages */
6682 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6683 { EM_LINESCROLL, 0, 1, FALSE },
6684 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6685 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6686 { WM_MOUSEHOVER, 0, 0, FALSE },
6687 { WM_MOUSEMOVE, 0, 0, FALSE },
6688 { WM_MOUSEWHEEL, 0, 0, FALSE },
6689 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6690 { WM_RBUTTONDOWN, 0, 0, TRUE },
6691 { WM_RBUTTONUP, 0, 0, TRUE },
6692 { WM_SETCURSOR, 0, 0, FALSE },
6693 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6694 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6695 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6696 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6697 { WM_LBUTTONUP, 0, 0, TRUE },
6698 /* hold down the right button and try some messages */
6699 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6700 { EM_LINESCROLL, 0, 1, FALSE },
6701 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6702 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6703 { WM_LBUTTONDOWN, 0, 0, TRUE },
6704 { WM_LBUTTONUP, 0, 0, TRUE },
6705 { WM_MOUSEHOVER, 0, 0, FALSE },
6706 { WM_MOUSEMOVE, 0, 0, TRUE },
6707 { WM_MOUSEWHEEL, 0, 0, FALSE },
6708 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6709 { WM_SETCURSOR, 0, 0, FALSE },
6710 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6711 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6712 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6713 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6714 { WM_RBUTTONUP, 0, 0, TRUE },
6715 /* try the messages with both buttons released */
6716 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6717 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6718 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6719 { WM_LBUTTONDOWN, 0, 0, TRUE },
6720 { WM_LBUTTONUP, 0, 0, TRUE },
6721 { WM_MOUSEHOVER, 0, 0, FALSE },
6722 { WM_MOUSEMOVE, 0, 0, TRUE },
6723 { WM_MOUSEWHEEL, 0, 0, FALSE },
6724 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6725 { WM_RBUTTONDOWN, 0, 0, TRUE },
6726 { WM_RBUTTONUP, 0, 0, TRUE },
6727 { WM_SETCURSOR, 0, 0, FALSE },
6728 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6729 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6730 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6731 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6732 };
6733
6734 /* register class to capture WM_NOTIFY */
6735 cls.style = 0;
6736 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6737 cls.cbClsExtra = 0;
6738 cls.cbWndExtra = 0;
6739 cls.hInstance = GetModuleHandleA(0);
6740 cls.hIcon = 0;
6741 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6742 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6743 cls.lpszMenuName = NULL;
6744 cls.lpszClassName = "EN_LINK_ParentClass";
6745 if(!RegisterClassA(&cls)) assert(0);
6746
6747 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6748 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6749 ok(parent != 0, "Failed to create parent window\n");
6750
6751 hwnd = new_richedit(parent);
6752 ok(hwnd != 0, "Failed to create edit window\n");
6753
6754 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6755
6756 cf2.cbSize = sizeof(CHARFORMAT2A);
6757 cf2.dwMask = CFM_LINK;
6758 cf2.dwEffects = CFE_LINK;
6759 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6760 /* mixing letters and numbers causes runs to be split */
6761 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6762
6763 GetCursorPos(&orig_cursor_pos);
6764 SetCursorPos(0, 0);
6765
6766 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6767 {
6768 link_notify_test("cursor position simulated", i, hwnd, parent,
6769 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6770 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6771 }
6772
6773 ClientToScreen(hwnd, &cursor_screen_pos);
6774 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6775
6776 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6777 {
6778 link_notify_test("cursor position set", i, hwnd, parent,
6779 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6780 link_notify_tests[i].notifies);
6781 }
6782
6783 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6784 DestroyWindow(hwnd);
6785 DestroyWindow(parent);
6786 }
6787
6788 static void test_undo_coalescing(void)
6789 {
6790 HWND hwnd;
6791 int result;
6792 char buffer[64] = {0};
6793
6794 /* multi-line control inserts CR normally */
6795 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6796 0, 0, 200, 60, 0, 0, 0, 0);
6797 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6798
6799 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6800 ok (result == FALSE, "Can undo after window creation.\n");
6801 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6802 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6803 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6804 ok (result == FALSE, "Can redo after window creation.\n");
6805 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6806 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6807
6808 /* Test the effect of arrows keys during typing on undo transactions*/
6809 simulate_typing_characters(hwnd, "one two three");
6810 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6811 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6812 simulate_typing_characters(hwnd, " four five six");
6813
6814 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6815 ok (result == FALSE, "Can redo before anything is undone.\n");
6816 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6817 ok (result == TRUE, "Cannot undo typed characters.\n");
6818 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6819 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6820 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6821 ok (result == TRUE, "Cannot redo after undo.\n");
6822 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6823 result = strcmp(buffer, "one two three");
6824 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6825
6826 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6827 ok (result == TRUE, "Cannot undo typed characters.\n");
6828 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6829 ok (result == TRUE, "Failed to undo typed characters.\n");
6830 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6831 result = strcmp(buffer, "");
6832 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6833
6834 /* Test the effect of focus changes during typing on undo transactions*/
6835 simulate_typing_characters(hwnd, "one two three");
6836 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6837 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6838 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6839 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6840 simulate_typing_characters(hwnd, " four five six");
6841 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6842 ok (result == TRUE, "Failed to undo typed characters.\n");
6843 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6844 result = strcmp(buffer, "one two three");
6845 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6846
6847 /* Test the effect of the back key during typing on undo transactions */
6848 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6849 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6850 ok (result == TRUE, "Failed to clear the text.\n");
6851 simulate_typing_characters(hwnd, "one two threa");
6852 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6853 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6854 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6855 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6856 simulate_typing_characters(hwnd, "e four five six");
6857 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6858 ok (result == TRUE, "Failed to undo typed characters.\n");
6859 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6860 result = strcmp(buffer, "");
6861 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6862
6863 /* Test the effect of the delete key during typing on undo transactions */
6864 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6865 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6866 ok(result == TRUE, "Failed to set the text.\n");
6867 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6868 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6869 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6870 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6871 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6872 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6873 ok (result == TRUE, "Failed to undo typed characters.\n");
6874 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6875 result = strcmp(buffer, "acd");
6876 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6877 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6878 ok (result == TRUE, "Failed to undo typed characters.\n");
6879 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6880 result = strcmp(buffer, "abcd");
6881 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6882
6883 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6884 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6885 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6886 ok (result == TRUE, "Failed to clear the text.\n");
6887 simulate_typing_characters(hwnd, "one two three");
6888 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6889 ok (result == 0, "expected %d but got %d\n", 0, result);
6890 simulate_typing_characters(hwnd, " four five six");
6891 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6892 ok (result == TRUE, "Failed to undo typed characters.\n");
6893 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6894 result = strcmp(buffer, "one two three");
6895 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6896 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6897 ok (result == TRUE, "Failed to undo typed characters.\n");
6898 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6899 result = strcmp(buffer, "");
6900 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6901
6902 DestroyWindow(hwnd);
6903 }
6904
6905 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6906 {
6907 int length;
6908
6909 /* MSDN lied, length is actually the number of bytes. */
6910 length = bytes / sizeof(WCHAR);
6911 switch(code)
6912 {
6913 case WB_ISDELIMITER:
6914 return text[pos] == 'X';
6915 case WB_LEFT:
6916 case WB_MOVEWORDLEFT:
6917 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6918 return pos-1;
6919 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6920 case WB_LEFTBREAK:
6921 pos--;
6922 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6923 pos--;
6924 return pos;
6925 case WB_RIGHT:
6926 case WB_MOVEWORDRIGHT:
6927 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6928 return pos+1;
6929 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6930 case WB_RIGHTBREAK:
6931 pos++;
6932 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6933 pos++;
6934 return pos;
6935 default:
6936 ok(FALSE, "Unexpected code %d\n", code);
6937 break;
6938 }
6939 return 0;
6940 }
6941
6942 static void test_word_movement(void)
6943 {
6944 HWND hwnd;
6945 int result;
6946 int sel_start, sel_end;
6947 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6948
6949 /* multi-line control inserts CR normally */
6950 hwnd = new_richedit(NULL);
6951
6952 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6953 ok (result == TRUE, "Failed to clear the text.\n");
6954 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6955 /* |one two three */
6956
6957 send_ctrl_key(hwnd, VK_RIGHT);
6958 /* one |two three */
6959 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6960 ok(sel_start == sel_end, "Selection should be empty\n");
6961 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6962
6963 send_ctrl_key(hwnd, VK_RIGHT);
6964 /* one two |three */
6965 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6966 ok(sel_start == sel_end, "Selection should be empty\n");
6967 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6968
6969 send_ctrl_key(hwnd, VK_LEFT);
6970 /* one |two three */
6971 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6972 ok(sel_start == sel_end, "Selection should be empty\n");
6973 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6974
6975 send_ctrl_key(hwnd, VK_LEFT);
6976 /* |one two three */
6977 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6978 ok(sel_start == sel_end, "Selection should be empty\n");
6979 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6980
6981 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6982 /* one two | three */
6983 send_ctrl_key(hwnd, VK_RIGHT);
6984 /* one two |three */
6985 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6986 ok(sel_start == sel_end, "Selection should be empty\n");
6987 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6988
6989 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6990 /* one two th|ree */
6991 send_ctrl_key(hwnd, VK_LEFT);
6992 /* one two |three */
6993 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6994 ok(sel_start == sel_end, "Selection should be empty\n");
6995 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6996
6997 /* Test with a custom word break procedure that uses X as the delimiter. */
6998 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6999 ok (result == TRUE, "Failed to clear the text.\n");
7000 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7001 /* |one twoXthree */
7002 send_ctrl_key(hwnd, VK_RIGHT);
7003 /* one twoX|three */
7004 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7005 ok(sel_start == sel_end, "Selection should be empty\n");
7006 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7007
7008 DestroyWindow(hwnd);
7009
7010 /* Make sure the behaviour is the same with a unicode richedit window,
7011 * and using unicode functions. */
7012
7013 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7014 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7015 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7016
7017 /* Test with a custom word break procedure that uses X as the delimiter. */
7018 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7019 ok (result == TRUE, "Failed to clear the text.\n");
7020 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7021 /* |one twoXthree */
7022 send_ctrl_key(hwnd, VK_RIGHT);
7023 /* one twoX|three */
7024 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7025 ok(sel_start == sel_end, "Selection should be empty\n");
7026 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7027
7028 DestroyWindow(hwnd);
7029 }
7030
7031 static void test_EM_CHARFROMPOS(void)
7032 {
7033 HWND hwnd;
7034 int result;
7035 RECT rcClient;
7036 POINTL point;
7037 point.x = 0;
7038 point.y = 40;
7039
7040 /* multi-line control inserts CR normally */
7041 hwnd = new_richedit(NULL);
7042 result = SendMessageA(hwnd, WM_SETTEXT, 0,
7043 (LPARAM)"one two three four five six seven\reight");
7044 ok(result == 1, "Expected 1, got %d\n", result);
7045 GetClientRect(hwnd, &rcClient);
7046
7047 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7048 ok(result == 34, "expected character index of 34 but got %d\n", result);
7049
7050 /* Test with points outside the bounds of the richedit control. */
7051 point.x = -1;
7052 point.y = 40;
7053 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7054 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7055
7056 point.x = 1000;
7057 point.y = 0;
7058 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7059 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
7060
7061 point.x = 1000;
7062 point.y = 36;
7063 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7064 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7065
7066 point.x = 1000;
7067 point.y = -1;
7068 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7069 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7070
7071 point.x = 1000;
7072 point.y = rcClient.bottom + 1;
7073 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7074 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7075
7076 point.x = 1000;
7077 point.y = rcClient.bottom;
7078 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7079 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7080
7081 DestroyWindow(hwnd);
7082 }
7083
7084 static void test_word_wrap(void)
7085 {
7086 HWND hwnd;
7087 POINTL point = {0, 60}; /* This point must be below the first line */
7088 const char *text = "Must be long enough to test line wrapping";
7089 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7090 int res, pos, lines;
7091
7092 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7093 * when specified on window creation and set later. */
7094 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7095 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7096 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7097 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7098 ok(res, "WM_SETTEXT failed.\n");
7099 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7100 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7101 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7102 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7103
7104 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
7105 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7106 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7107 DestroyWindow(hwnd);
7108
7109 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7110 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7111 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7112
7113 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7114 ok(res, "WM_SETTEXT failed.\n");
7115 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7116 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7117 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7118 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7119
7120 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7121 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7122 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7123 DestroyWindow(hwnd);
7124
7125 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7126 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7127 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7128 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7129 ok(res, "WM_SETTEXT failed.\n");
7130 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7131 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7132
7133 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7134 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7135 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7136 DestroyWindow(hwnd);
7137
7138 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7139 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7140 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7141 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7142 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7143 ok(res, "WM_SETTEXT failed.\n");
7144 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7145 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7146
7147 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7148 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7149 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7150
7151 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7152 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7153 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7154 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7155 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7156
7157 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7158 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7159 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7160 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7161 DestroyWindow(hwnd);
7162
7163 /* Test to see if wrapping happens with redraw disabled. */
7164 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7165 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7166 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7167 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7168 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7169 ok(res, "EM_REPLACESEL failed.\n");
7170 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7171 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7172 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7173 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7174 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7175
7176 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7177 DestroyWindow(hwnd);
7178 }
7179
7180 static void test_autoscroll(void)
7181 {
7182 HWND hwnd = new_richedit(NULL);
7183 int lines, ret, redraw;
7184 POINT pt;
7185
7186 for (redraw = 0; redraw <= 1; redraw++) {
7187 trace("testing with WM_SETREDRAW=%d\n", redraw);
7188 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7189 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7190 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7191 ok(lines == 8, "%d lines instead of 8\n", lines);
7192 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7193 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7194 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7195 ret = GetWindowLongA(hwnd, GWL_STYLE);
7196 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7197
7198 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7199 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7200 ok(lines == 1, "%d lines instead of 1\n", lines);
7201 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7202 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7203 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7204 ret = GetWindowLongA(hwnd, GWL_STYLE);
7205 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7206 }
7207
7208 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7209 DestroyWindow(hwnd);
7210
7211 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7212 * auto vertical/horizontal scrolling options. */
7213 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7214 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7215 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7216 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7217 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7218 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7219 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7220 ret = GetWindowLongA(hwnd, GWL_STYLE);
7221 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7222 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7223 DestroyWindow(hwnd);
7224
7225 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7226 WS_POPUP|ES_MULTILINE,
7227 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7228 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7229 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7230 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7231 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7232 ret = GetWindowLongA(hwnd, GWL_STYLE);
7233 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7234 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7235 DestroyWindow(hwnd);
7236 }
7237
7238
7239 static void test_format_rect(void)
7240 {
7241 HWND hwnd;
7242 RECT rc, expected, clientRect;
7243 int n;
7244 DWORD options;
7245
7246 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7247 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7248 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7249 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7250
7251 GetClientRect(hwnd, &clientRect);
7252
7253 expected = clientRect;
7254 InflateRect(&expected, -1, 0);
7255 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7256 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7257 wine_dbgstr_rect(&expected));
7258
7259 for (n = -3; n <= 3; n++)
7260 {
7261 rc = clientRect;
7262 InflateRect(&rc, -n, -n);
7263 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7264
7265 expected = rc;
7266 expected.top = max(0, rc.top);
7267 expected.left = max(0, rc.left);
7268 expected.bottom = min(clientRect.bottom, rc.bottom);
7269 expected.right = min(clientRect.right, rc.right);
7270 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7271 ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7272 wine_dbgstr_rect(&expected));
7273 }
7274
7275 rc = clientRect;
7276 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7277 expected = clientRect;
7278 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7279 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7280 wine_dbgstr_rect(&expected));
7281
7282 /* Adding the selectionbar adds the selectionbar width to the left side. */
7283 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7284 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7285 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7286 expected.left += 8; /* selection bar width */
7287 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7288 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7289 wine_dbgstr_rect(&expected));
7290
7291 rc = clientRect;
7292 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7293 expected = clientRect;
7294 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7295 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7296 wine_dbgstr_rect(&expected));
7297
7298 /* Removing the selectionbar subtracts the selectionbar width from the left side,
7299 * even if the left side is already 0. */
7300 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7301 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7302 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7303 expected.left -= 8; /* selection bar width */
7304 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7305 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7306 wine_dbgstr_rect(&expected));
7307
7308 /* Set the absolute value of the formatting rectangle. */
7309 rc = clientRect;
7310 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7311 expected = clientRect;
7312 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7313 ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7314 wine_dbgstr_rect(&expected));
7315
7316 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7317 * LPARAM as being a relative offset when the WPARAM value is 1, but these
7318 * tests show that this isn't true. */
7319 rc.top = 15;
7320 rc.left = 15;
7321 rc.bottom = clientRect.bottom - 15;
7322 rc.right = clientRect.right - 15;
7323 expected = rc;
7324 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7325 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7326 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7327 wine_dbgstr_rect(&expected));
7328
7329 /* For some reason it does not limit the values to the client rect with
7330 * a WPARAM value of 1. */
7331 rc.top = -15;
7332 rc.left = -15;
7333 rc.bottom = clientRect.bottom + 15;
7334 rc.right = clientRect.right + 15;
7335 expected = rc;
7336 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7337 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7338 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7339 wine_dbgstr_rect(&expected));
7340
7341 /* Reset to default rect and check how the format rect adjusts to window
7342 * resize and how it copes with very small windows */
7343 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7344
7345 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7346 GetClientRect(hwnd, &clientRect);
7347
7348 expected = clientRect;
7349 InflateRect(&expected, -1, 0);
7350 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7351 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7352 wine_dbgstr_rect(&expected));
7353
7354 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7355 GetClientRect(hwnd, &clientRect);
7356
7357 expected = clientRect;
7358 InflateRect(&expected, -1, 0);
7359 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7360 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7361 wine_dbgstr_rect(&expected));
7362
7363 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7364 GetClientRect(hwnd, &clientRect);
7365
7366 expected = clientRect;
7367 InflateRect(&expected, -1, 0);
7368 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7369 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7370 wine_dbgstr_rect(&expected));
7371
7372 DestroyWindow(hwnd);
7373
7374 /* The extended window style affects the formatting rectangle. */
7375 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7376 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7377 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7378 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7379
7380 GetClientRect(hwnd, &clientRect);
7381
7382 expected = clientRect;
7383 expected.top += 1;
7384 InflateRect(&expected, -1, 0);
7385 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7386 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7387 wine_dbgstr_rect(&expected));
7388
7389 rc = clientRect;
7390 InflateRect(&rc, -5, -5);
7391 expected = rc;
7392 expected.top -= 1;
7393 InflateRect(&expected, 1, 0);
7394 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7395 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7396 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7397 wine_dbgstr_rect(&expected));
7398
7399 DestroyWindow(hwnd);
7400 }
7401
7402 static void test_WM_GETDLGCODE(void)
7403 {
7404 HWND hwnd;
7405 UINT res, expected;
7406 MSG msg;
7407
7408 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7409
7410 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7411 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7412 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7413 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7414 msg.hwnd = hwnd;
7415 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7416 expected = expected | DLGC_WANTMESSAGE;
7417 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7418 res, expected);
7419 DestroyWindow(hwnd);
7420
7421 msg.message = WM_KEYDOWN;
7422 msg.wParam = VK_RETURN;
7423 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7424 msg.pt.x = 0;
7425 msg.pt.y = 0;
7426 msg.time = GetTickCount();
7427
7428 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7429 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7430 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7431 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7432 msg.hwnd = hwnd;
7433 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7434 expected = expected | DLGC_WANTMESSAGE;
7435 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7436 res, expected);
7437 DestroyWindow(hwnd);
7438
7439 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7440 ES_MULTILINE|WS_POPUP,
7441 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7442 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7443 msg.hwnd = hwnd;
7444 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7445 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7446 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7447 res, expected);
7448 DestroyWindow(hwnd);
7449
7450 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7451 ES_WANTRETURN|WS_POPUP,
7452 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7453 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7454 msg.hwnd = hwnd;
7455 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7456 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7457 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7458 res, expected);
7459 DestroyWindow(hwnd);
7460
7461 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7462 WS_POPUP,
7463 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7464 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7465 msg.hwnd = hwnd;
7466 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7467 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7468 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7469 res, expected);
7470 DestroyWindow(hwnd);
7471
7472 msg.wParam = VK_TAB;
7473 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7474
7475 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7476 ES_MULTILINE|WS_POPUP,
7477 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7478 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7479 msg.hwnd = hwnd;
7480 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7481 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7482 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7483 res, expected);
7484 DestroyWindow(hwnd);
7485
7486 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7487 WS_POPUP,
7488 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7489 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7490 msg.hwnd = hwnd;
7491 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7492 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7493 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7494 res, expected);
7495 DestroyWindow(hwnd);
7496
7497 hold_key(VK_CONTROL);
7498
7499 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7500 ES_MULTILINE|WS_POPUP,
7501 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7502 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7503 msg.hwnd = hwnd;
7504 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7505 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7506 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7507 res, expected);
7508 DestroyWindow(hwnd);
7509
7510 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7511 WS_POPUP,
7512 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7513 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7514 msg.hwnd = hwnd;
7515 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7516 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7517 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7518 res, expected);
7519 DestroyWindow(hwnd);
7520
7521 release_key(VK_CONTROL);
7522
7523 msg.wParam = 'a';
7524 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7525
7526 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7527 ES_MULTILINE|WS_POPUP,
7528 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7529 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7530 msg.hwnd = hwnd;
7531 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7532 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7533 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7534 res, expected);
7535 DestroyWindow(hwnd);
7536
7537 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7538 WS_POPUP,
7539 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7540 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7541 msg.hwnd = hwnd;
7542 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7543 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7544 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7545 res, expected);
7546 DestroyWindow(hwnd);
7547
7548 msg.message = WM_CHAR;
7549
7550 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7551 ES_MULTILINE|WS_POPUP,
7552 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7553 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7554 msg.hwnd = hwnd;
7555 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7556 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7557 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7558 res, expected);
7559 DestroyWindow(hwnd);
7560
7561 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7562 WS_POPUP,
7563 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7564 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7565 msg.hwnd = hwnd;
7566 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7567 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7568 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7569 res, expected);
7570 DestroyWindow(hwnd);
7571
7572 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7573 WS_POPUP|ES_SAVESEL,
7574 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7575 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7576 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7577 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7578 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7579 res, expected);
7580 DestroyWindow(hwnd);
7581 }
7582
7583 static void test_zoom(void)
7584 {
7585 HWND hwnd;
7586 UINT ret;
7587 RECT rc;
7588 POINT pt;
7589 int numerator, denominator;
7590
7591 hwnd = new_richedit(NULL);
7592 GetClientRect(hwnd, &rc);
7593 pt.x = (rc.right - rc.left) / 2;
7594 pt.y = (rc.bottom - rc.top) / 2;
7595 ClientToScreen(hwnd, &pt);
7596
7597 /* Test initial zoom value */
7598 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7599 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7600 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7601 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7602
7603 /* test scroll wheel */
7604 hold_key(VK_CONTROL);
7605 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7606 MAKELPARAM(pt.x, pt.y));
7607 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7608 release_key(VK_CONTROL);
7609
7610 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7611 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7612 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7613 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7614
7615 /* Test how much the mouse wheel can zoom in and out. */
7616 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7617 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7618
7619 hold_key(VK_CONTROL);
7620 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7621 MAKELPARAM(pt.x, pt.y));
7622 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7623 release_key(VK_CONTROL);
7624
7625 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7626 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7627 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7628 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7629
7630 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7631 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7632
7633 hold_key(VK_CONTROL);
7634 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7635 MAKELPARAM(pt.x, pt.y));
7636 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7637 release_key(VK_CONTROL);
7638
7639 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7640 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7641 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7642 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7643
7644 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7645 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7646
7647 hold_key(VK_CONTROL);
7648 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7649 MAKELPARAM(pt.x, pt.y));
7650 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7651 release_key(VK_CONTROL);
7652
7653 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7654 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7655 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7656 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7657
7658 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7659 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7660
7661 hold_key(VK_CONTROL);
7662 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7663 MAKELPARAM(pt.x, pt.y));
7664 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7665 release_key(VK_CONTROL);
7666
7667 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7668 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7669 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7670 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7671
7672 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7673 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7674 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7675
7676 hold_key(VK_CONTROL);
7677 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7678 MAKELPARAM(pt.x, pt.y));
7679 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7680 release_key(VK_CONTROL);
7681
7682 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7683 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7684 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7685 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7686
7687 /* Test bounds checking on EM_SETZOOM */
7688 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7689 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7690
7691 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7692 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7693
7694 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7695 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7696
7697 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7698 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7699 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7700 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7701
7702 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7703 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7704
7705 /* See if negative numbers are accepted. */
7706 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7707 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7708
7709 /* See if negative numbers are accepted. */
7710 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7711 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7712
7713 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7714 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7715 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7716 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7717
7718 /* Reset the zoom value */
7719 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7720 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7721
7722 DestroyWindow(hwnd);
7723 }
7724
7725 struct dialog_mode_messages
7726 {
7727 int wm_getdefid, wm_close, wm_nextdlgctl;
7728 };
7729
7730 static struct dialog_mode_messages dm_messages;
7731
7732 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7733 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7734 "got %d\n", wmclose, dm_messages.wm_close); \
7735 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7736 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7737 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7738 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7739
7740 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7741 {
7742 switch (iMsg)
7743 {
7744 case DM_GETDEFID:
7745 dm_messages.wm_getdefid++;
7746 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7747 case WM_NEXTDLGCTL:
7748 dm_messages.wm_nextdlgctl++;
7749 break;
7750 case WM_CLOSE:
7751 dm_messages.wm_close++;
7752 break;
7753 }
7754
7755 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7756 }
7757
7758 static void test_dialogmode(void)
7759 {
7760 HWND hwRichEdit, hwParent, hwButton;
7761 MSG msg= {0};
7762 int lcount, r;
7763 WNDCLASSA cls;
7764
7765 cls.style = 0;
7766 cls.lpfnWndProc = dialog_mode_wnd_proc;
7767 cls.cbClsExtra = 0;
7768 cls.cbWndExtra = 0;
7769 cls.hInstance = GetModuleHandleA(0);
7770 cls.hIcon = 0;
7771 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7772 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7773 cls.lpszMenuName = NULL;
7774 cls.lpszClassName = "DialogModeParentClass";
7775 if(!RegisterClassA(&cls)) assert(0);
7776
7777 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7778 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7779
7780 /* Test richedit(ES_MULTILINE) */
7781
7782 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7783
7784 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7785 ok(0 == r, "expected 0, got %d\n", r);
7786 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7787 ok(2 == lcount, "expected 2, got %d\n", lcount);
7788
7789 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7790 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7791
7792 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7793 ok(0 == r, "expected 0, got %d\n", r);
7794 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7795 ok(3 == lcount, "expected 3, got %d\n", lcount);
7796
7797 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7798 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7799 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7800 ok(0 == r, "expected 0, got %d\n", r);
7801 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7802 ok(3 == lcount, "expected 3, got %d\n", lcount);
7803
7804 DestroyWindow(hwRichEdit);
7805
7806 /* Test standalone richedit(ES_MULTILINE) */
7807
7808 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7809
7810 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7811 ok(0 == r, "expected 0, got %d\n", r);
7812 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7813 ok(2 == lcount, "expected 2, got %d\n", lcount);
7814
7815 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7816 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7817
7818 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7819 ok(0 == r, "expected 0, got %d\n", r);
7820 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7821 ok(2 == lcount, "expected 2, got %d\n", lcount);
7822
7823 DestroyWindow(hwRichEdit);
7824
7825 /* Check a destination for messages */
7826
7827 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7828
7829 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7830 SetParent( hwRichEdit, NULL);
7831
7832 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7833 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7834
7835 memset(&dm_messages, 0, sizeof(dm_messages));
7836 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7837 ok(0 == r, "expected 0, got %d\n", r);
7838 test_dm_messages(0, 1, 0);
7839
7840 memset(&dm_messages, 0, sizeof(dm_messages));
7841 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7842 ok(0 == r, "expected 0, got %d\n", r);
7843 test_dm_messages(0, 0, 1);
7844
7845 DestroyWindow(hwRichEdit);
7846
7847 /* Check messages from richedit(ES_MULTILINE) */
7848
7849 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7850
7851 memset(&dm_messages, 0, sizeof(dm_messages));
7852 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7853 ok(0 == r, "expected 0, got %d\n", r);
7854 test_dm_messages(0, 0, 0);
7855
7856 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7857 ok(2 == lcount, "expected 2, got %d\n", lcount);
7858
7859 memset(&dm_messages, 0, sizeof(dm_messages));
7860 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7861 ok(0 == r, "expected 0, got %d\n", r);
7862 test_dm_messages(0, 0, 0);
7863
7864 memset(&dm_messages, 0, sizeof(dm_messages));
7865 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7866 ok(0 == r, "expected 0, got %d\n", r);
7867 test_dm_messages(0, 0, 0);
7868
7869 memset(&dm_messages, 0, sizeof(dm_messages));
7870 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7871 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7872 test_dm_messages(0, 0, 0);
7873
7874 memset(&dm_messages, 0, sizeof(dm_messages));
7875 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7876 ok(0 == r, "expected 0, got %d\n", r);
7877 test_dm_messages(0, 1, 0);
7878
7879 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7880 ok(2 == lcount, "expected 2, got %d\n", lcount);
7881
7882 memset(&dm_messages, 0, sizeof(dm_messages));
7883 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7884 ok(0 == r, "expected 0, got %d\n", r);
7885 test_dm_messages(0, 0, 0);
7886
7887 memset(&dm_messages, 0, sizeof(dm_messages));
7888 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7889 ok(0 == r, "expected 0, got %d\n", r);
7890 test_dm_messages(0, 0, 1);
7891
7892 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7893 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7894 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7895
7896 memset(&dm_messages, 0, sizeof(dm_messages));
7897 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7898 ok(0 == r, "expected 0, got %d\n", r);
7899 test_dm_messages(0, 1, 1);
7900
7901 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7902 ok(2 == lcount, "expected 2, got %d\n", lcount);
7903
7904 DestroyWindow(hwButton);
7905 DestroyWindow(hwRichEdit);
7906
7907 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7908
7909 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7910
7911 memset(&dm_messages, 0, sizeof(dm_messages));
7912 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7913 ok(0 == r, "expected 0, got %d\n", r);
7914 test_dm_messages(0, 0, 0);
7915
7916 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7917 ok(2 == lcount, "expected 2, got %d\n", lcount);
7918
7919 memset(&dm_messages, 0, sizeof(dm_messages));
7920 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7921 ok(0 == r, "expected 0, got %d\n", r);
7922 test_dm_messages(0, 0, 0);
7923
7924 memset(&dm_messages, 0, sizeof(dm_messages));
7925 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7926 ok(0 == r, "expected 0, got %d\n", r);
7927 test_dm_messages(0, 0, 0);
7928
7929 memset(&dm_messages, 0, sizeof(dm_messages));
7930 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7931 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7932 test_dm_messages(0, 0, 0);
7933
7934 memset(&dm_messages, 0, sizeof(dm_messages));
7935 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7936 ok(0 == r, "expected 0, got %d\n", r);
7937 test_dm_messages(0, 0, 0);
7938
7939 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7940 ok(3 == lcount, "expected 3, got %d\n", lcount);
7941
7942 memset(&dm_messages, 0, sizeof(dm_messages));
7943 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7944 ok(0 == r, "expected 0, got %d\n", r);
7945 test_dm_messages(0, 0, 0);
7946
7947 memset(&dm_messages, 0, sizeof(dm_messages));
7948 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7949 ok(0 == r, "expected 0, got %d\n", r);
7950 test_dm_messages(0, 0, 1);
7951
7952 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7953 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7954 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7955
7956 memset(&dm_messages, 0, sizeof(dm_messages));
7957 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7958 ok(0 == r, "expected 0, got %d\n", r);
7959 test_dm_messages(0, 0, 0);
7960
7961 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7962 ok(4 == lcount, "expected 4, got %d\n", lcount);
7963
7964 DestroyWindow(hwButton);
7965 DestroyWindow(hwRichEdit);
7966
7967 /* Check messages from richedit(0) */
7968
7969 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7970
7971 memset(&dm_messages, 0, sizeof(dm_messages));
7972 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7973 ok(0 == r, "expected 0, got %d\n", r);
7974 test_dm_messages(0, 0, 0);
7975
7976 memset(&dm_messages, 0, sizeof(dm_messages));
7977 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7978 ok(0 == r, "expected 0, got %d\n", r);
7979 test_dm_messages(0, 0, 0);
7980
7981 memset(&dm_messages, 0, sizeof(dm_messages));
7982 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7983 ok(0 == r, "expected 0, got %d\n", r);
7984 test_dm_messages(0, 0, 0);
7985
7986 memset(&dm_messages, 0, sizeof(dm_messages));
7987 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7988 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7989 test_dm_messages(0, 0, 0);
7990
7991 memset(&dm_messages, 0, sizeof(dm_messages));
7992 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7993 ok(0 == r, "expected 0, got %d\n", r);
7994 test_dm_messages(0, 1, 0);
7995
7996 memset(&dm_messages, 0, sizeof(dm_messages));
7997 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7998 ok(0 == r, "expected 0, got %d\n", r);
7999 test_dm_messages(0, 0, 0);
8000
8001 memset(&dm_messages, 0, sizeof(dm_messages));
8002 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8003 ok(0 == r, "expected 0, got %d\n", r);
8004 test_dm_messages(0, 0, 1);
8005
8006 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8007 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8008 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8009
8010 memset(&dm_messages, 0, sizeof(dm_messages));
8011 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8012 ok(0 == r, "expected 0, got %d\n", r);
8013 test_dm_messages(0, 1, 1);
8014
8015 DestroyWindow(hwRichEdit);
8016
8017 /* Check messages from richedit(ES_WANTRETURN) */
8018
8019 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8020
8021 memset(&dm_messages, 0, sizeof(dm_messages));
8022 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8023 ok(0 == r, "expected 0, got %d\n", r);
8024 test_dm_messages(0, 0, 0);
8025
8026 memset(&dm_messages, 0, sizeof(dm_messages));
8027 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8028 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8029 test_dm_messages(0, 0, 0);
8030
8031 memset(&dm_messages, 0, sizeof(dm_messages));
8032 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8033 ok(0 == r, "expected 0, got %d\n", r);
8034 test_dm_messages(0, 0, 0);
8035
8036 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8037 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8038 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8039
8040 memset(&dm_messages, 0, sizeof(dm_messages));
8041 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8042 ok(0 == r, "expected 0, got %d\n", r);
8043 test_dm_messages(0, 0, 0);
8044
8045 DestroyWindow(hwRichEdit);
8046 DestroyWindow(hwParent);
8047 }
8048
8049 static void test_EM_FINDWORDBREAK_W(void)
8050 {
8051 static const struct {
8052 WCHAR c;
8053 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8054 } delimiter_tests[] = {
8055 {0x0a, FALSE}, /* newline */
8056 {0x0b, FALSE}, /* vertical tab */
8057 {0x0c, FALSE}, /* form feed */
8058 {0x0d, FALSE}, /* carriage return */
8059 {0x20, TRUE}, /* space */
8060 {0x61, FALSE}, /* capital letter a */
8061 {0xa0, FALSE}, /* no-break space */
8062 {0x2000, FALSE}, /* en quad */
8063 {0x3000, FALSE}, /* Ideographic space */
8064 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8065 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8066 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8067 {0xac00, FALSE}, /* Hangul character GA*/
8068 {0xd7af, FALSE}, /* End of Hangul character chart */
8069 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
8070 {0xff20, FALSE}, /* fullwidth commercial @ */
8071 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
8072 };
8073 int i;
8074 HWND hwndRichEdit = new_richeditW(NULL);
8075 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8076 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8077 {
8078 WCHAR wbuf[2];
8079 int result;
8080
8081 wbuf[0] = delimiter_tests[i].c;
8082 wbuf[1] = 0;
8083 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8084 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8085 todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8086 ok(result == delimiter_tests[i].isdelimiter,
8087 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8088 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8089 }
8090 DestroyWindow(hwndRichEdit);
8091 }
8092
8093 static void test_EM_FINDWORDBREAK_A(void)
8094 {
8095 static const struct {
8096 WCHAR c;
8097 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8098 } delimiter_tests[] = {
8099 {0x0a, FALSE}, /* newline */
8100 {0x0b, FALSE}, /* vertical tab */
8101 {0x0c, FALSE}, /* form feed */
8102 {0x0d, FALSE}, /* carriage return */
8103 {0x20, TRUE}, /* space */
8104 {0x61, FALSE}, /* capital letter a */
8105 };
8106 int i;
8107 HWND hwndRichEdit = new_richedit(NULL);
8108
8109 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8110 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8111 {
8112 int result;
8113 char buf[2];
8114 buf[0] = delimiter_tests[i].c;
8115 buf[1] = 0;
8116 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8117 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8118 todo_wine_if (buf[0] == 0x20)
8119 ok(result == delimiter_tests[i].isdelimiter,
8120 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8121 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8122 }
8123 DestroyWindow(hwndRichEdit);
8124 }
8125
8126 /*
8127 * This test attempts to show the effect of enter on a richedit
8128 * control v1.0 inserts CRLF whereas for higher versions it only
8129 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8130 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8131 * does for higher. The same test is cloned in riched32 and riched20.
8132 */
8133 static void test_enter(void)
8134 {
8135 static const struct {
8136 const char *initialtext;
8137 const int cursor;
8138 const char *expectedwmtext;
8139 const char *expectedemtext;
8140 const char *expectedemtextcrlf;
8141 } testenteritems[] = {
8142 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8143 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8144 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8145 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8146 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8147 };
8148
8149 char expectedbuf[1024];
8150 char resultbuf[1024];
8151 HWND hwndRichEdit = new_richedit(NULL);
8152 UINT i,j;
8153
8154 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
8155
8156 char buf[1024] = {0};
8157 LRESULT result;
8158 GETTEXTEX getText;
8159 const char *expected;
8160
8161 /* Set the text to the initial text */
8162 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8163 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8164
8165 /* Send Enter */
8166 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8167 simulate_typing_characters(hwndRichEdit, "\r");
8168
8169 /* 1. Retrieve with WM_GETTEXT */
8170 buf[0] = 0x00;
8171 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8172 expected = testenteritems[i].expectedwmtext;
8173
8174 resultbuf[0]=0x00;
8175 for (j = 0; j < (UINT)result; j++)
8176 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8177 expectedbuf[0] = '\0';
8178 for (j = 0; j < strlen(expected); j++)
8179 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8180
8181 result = strcmp(expected, buf);
8182 ok (result == 0,
8183 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8184 i, resultbuf, expectedbuf);
8185
8186 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8187 getText.cb = sizeof(buf);
8188 getText.flags = GT_DEFAULT;
8189 getText.codepage = CP_ACP;
8190 getText.lpDefaultChar = NULL;
8191 getText.lpUsedDefChar = NULL;
8192 buf[0] = 0x00;
8193 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8194 expected = testenteritems[i].expectedemtext;
8195
8196 resultbuf[0]=0x00;
8197 for (j = 0; j < (UINT)result; j++)
8198 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8199 expectedbuf[0] = '\0';
8200 for (j = 0; j < strlen(expected); j++)
8201 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8202
8203 result = strcmp(expected, buf);
8204 ok (result == 0,
8205 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8206 i, resultbuf, expectedbuf);
8207
8208 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8209 getText.cb = sizeof(buf);
8210 getText.flags = GT_USECRLF;
8211 getText.codepage = CP_ACP;
8212 getText.lpDefaultChar = NULL;
8213 getText.lpUsedDefChar = NULL;
8214 buf[0] = 0x00;
8215 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8216 expected = testenteritems[i].expectedemtextcrlf;
8217
8218 resultbuf[0]=0x00;
8219 for (j = 0; j < (UINT)result; j++)
8220 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8221 expectedbuf[0] = '\0';
8222 for (j = 0; j < strlen(expected); j++)
8223 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8224
8225 result = strcmp(expected, buf);
8226 ok (result == 0,
8227 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8228 i, resultbuf, expectedbuf);
8229 }
8230
8231 DestroyWindow(hwndRichEdit);
8232 }
8233
8234 static void test_WM_CREATE(void)
8235 {
8236 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8237 static const char title[] = "line1\nline2";
8238
8239 HWND rich_edit;
8240 LRESULT res;
8241 char buf[64];
8242 int len;
8243
8244 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8245 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8246 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8247
8248 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8249 ok(len == 5, "GetWindowText returned %d\n", len);
8250 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8251
8252 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8253 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8254
8255 DestroyWindow(rich_edit);
8256
8257 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8258 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8259 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8260
8261 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8262 ok(len == 12, "GetWindowText returned %d\n", len);
8263 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8264
8265 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8266 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8267
8268 DestroyWindow(rich_edit);
8269 }
8270
8271 /*******************************************************************
8272 * Test that after deleting all of the text, the first paragraph
8273 * format reverts to the default.
8274 */
8275 static void test_reset_default_para_fmt( void )
8276 {
8277 HWND richedit = new_richeditW( NULL );
8278 PARAFORMAT2 fmt;
8279 WORD def_align, new_align;
8280
8281 memset( &fmt, 0, sizeof(fmt) );
8282 fmt.cbSize = sizeof(PARAFORMAT2);
8283 fmt.dwMask = -1;
8284 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8285 def_align = fmt.wAlignment;
8286 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8287
8288 simulate_typing_characters( richedit, "123" );
8289
8290 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8291 fmt.dwMask = PFM_ALIGNMENT;
8292 fmt.wAlignment = new_align;
8293 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8294
8295 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8296 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8297
8298 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8299 SendMessageA( richedit, WM_CUT, 0, 0 );
8300
8301 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8302 ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8303
8304 DestroyWindow( richedit );
8305 }
8306
8307 static void test_EM_SETREADONLY(void)
8308 {
8309 HWND richedit = new_richeditW(NULL);
8310 DWORD dwStyle;
8311 LRESULT res;
8312
8313 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8314 ok(res == 1, "EM_SETREADONLY\n");
8315 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8316 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8317
8318 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8319 ok(res == 1, "EM_SETREADONLY\n");
8320 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8321 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8322
8323 DestroyWindow(richedit);
8324 }
8325
8326 static inline LONG twips2points(LONG value)
8327 {
8328 return value / 20;
8329 }
8330
8331 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8332 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8333 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8334 LRESULT expected_res, BOOL expected_undo)
8335 {
8336 CHARFORMAT2A cf;
8337 LRESULT res;
8338 BOOL isundo;
8339
8340 cf.cbSize = sizeof(cf);
8341 cf.dwMask = CFM_SIZE;
8342
8343 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8344 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8345 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8346 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8347 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8348 twips2points(cf.yHeight), expected_size);
8349 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8350 isundo, expected_undo);
8351 }
8352
8353 static void test_EM_SETFONTSIZE(void)
8354 {
8355 HWND richedit = new_richedit(NULL);
8356 CHAR text[] = "wine";
8357 CHARFORMAT2A tmp_cf;
8358 LONG default_size;
8359
8360 tmp_cf.cbSize = sizeof(tmp_cf);
8361 tmp_cf.dwMask = CFM_SIZE;
8362 tmp_cf.yHeight = 9 * 20.0;
8363 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8364
8365 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8366
8367 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8368 /* without selection */
8369 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8370 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8371 default_size = twips2points(tmp_cf.yHeight);
8372 ok(default_size == 9, "Default font size should not be changed.\n");
8373 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8374
8375 SendMessageA(richedit, EM_SETSEL, 0, 2);
8376
8377 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8378
8379 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8380 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8381 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8382
8383 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8384 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8385 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8386 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8387 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8388 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8389 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8390 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8391 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8392 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8393 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8394 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8395 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8396 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8397 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8398 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8399 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8400 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8401 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8402 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8403 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8404 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8405 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8406 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8407 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8408 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8409 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8410 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8411
8412 /* return FALSE when richedit is TM_PLAINTEXT mode */
8413 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8414 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8415 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8416
8417 DestroyWindow(richedit);
8418 }
8419
8420 static void test_alignment_style(void)
8421 {
8422 HWND richedit = NULL;
8423 PARAFORMAT2 pf;
8424 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8425 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8426 ES_LEFT | ES_RIGHT | ES_CENTER};
8427 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8428 PFA_RIGHT, PFA_CENTER};
8429 const char * streamtext =
8430 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8431 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8432 "}\r\n";
8433 EDITSTREAM es;
8434 int i;
8435
8436 for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8437 {
8438 DWORD dwStyle, new_align;
8439
8440 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8441 memset(&pf, 0, sizeof(pf));
8442 pf.cbSize = sizeof(PARAFORMAT2);
8443 pf.dwMask = -1;
8444
8445 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8446 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8447 i, pf.wAlignment, align_mask[i]);
8448 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8449 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8450 "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8451
8452
8453 /* Based on test_reset_default_para_fmt() */
8454 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8455 simulate_typing_characters(richedit, "123");
8456
8457 SendMessageW(richedit, EM_SETSEL, 0, -1);
8458 pf.dwMask = PFM_ALIGNMENT;
8459 pf.wAlignment = new_align;
8460 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8461
8462 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8463 ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8464
8465 SendMessageW(richedit, EM_SETSEL, 0, -1);
8466 SendMessageW(richedit, WM_CUT, 0, 0);
8467
8468 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8469 ok(pf.wAlignment == align_mask[i], "got %d expect %d\n", pf.wAlignment, align_mask[i]);
8470
8471 DestroyWindow(richedit);
8472 }
8473
8474 /* test with EM_STREAMIN */
8475 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8476 simulate_typing_characters(richedit, "abc");
8477 es.dwCookie = (DWORD_PTR)&streamtext;
8478 es.dwError = 0;
8479 es.pfnCallback = test_EM_STREAMIN_esCallback;
8480 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8481 SendMessageW(richedit, EM_SETSEL, 0, -1);
8482 memset(&pf, 0, sizeof(pf));
8483 pf.cbSize = sizeof(PARAFORMAT2);
8484 pf.dwMask = -1;
8485 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8486 ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8487 DestroyWindow(richedit);
8488 }
8489
8490 static void test_WM_GETTEXTLENGTH(void)
8491 {
8492 HWND hwndRichEdit = new_richedit(NULL);
8493 static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8494 static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8495 static const char text3[] = "abcdef\x8e\xf0";
8496 int result;
8497
8498 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8499 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8500 ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8501 result, lstrlenA(text1));
8502
8503 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8504 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8505 ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8506 result, lstrlenA(text2));
8507
8508 /* Test with multibyte character */
8509 if (!is_lang_japanese)
8510 skip("Skip multibyte character tests on non-Japanese platform\n");
8511 else
8512 {
8513 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8514 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8515 todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8516 }
8517
8518 DestroyWindow(hwndRichEdit);
8519 }
8520
8521 static void test_rtf_specials(void)
8522 {
8523 const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8524 "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8525 const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8526 0x201d,0x200e,0x200f,0x200d,0x200c};
8527 const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8528 HWND edit = new_richeditW( NULL );
8529 EDITSTREAM es;
8530 WCHAR buf[80];
8531 LRESULT result;
8532 PARAFORMAT2 fmt;
8533
8534 es.dwCookie = (DWORD_PTR)&specials;
8535 es.dwError = 0;
8536 es.pfnCallback = test_EM_STREAMIN_esCallback;
8537 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8538 ok( result == 11, "got %ld\n", result );
8539
8540 result = SendMessageW( edit, WM_GETTEXT, sizeof(buf)/sizeof(buf[0]), (LPARAM)buf );
8541 ok( result == sizeof(expect_specials)/sizeof(expect_specials[0]), "got %ld\n", result );
8542 ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8543
8544 /* Show that \rtlpar propagates to the second paragraph and is
8545 reset by \pard in the third. */
8546 es.dwCookie = (DWORD_PTR)&pard;
8547 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8548 ok( result == 11, "got %ld\n", result );
8549
8550 fmt.cbSize = sizeof(fmt);
8551 SendMessageW( edit, EM_SETSEL, 1, 1 );
8552 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8553 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8554 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8555 SendMessageW( edit, EM_SETSEL, 5, 5 );
8556 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8557 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8558 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8559 SendMessageW( edit, EM_SETSEL, 9, 9 );
8560 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8561 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8562 ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8563
8564 DestroyWindow( edit );
8565 }
8566
8567 static void test_background(void)
8568 {
8569 HWND hwndRichEdit = new_richedit(NULL);
8570
8571 /* set the background color to black */
8572 ValidateRect(hwndRichEdit, NULL);
8573 SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8574 ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8575
8576 DestroyWindow(hwndRichEdit);
8577 }
8578
8579 static void test_eop_char_fmt(void)
8580 {
8581 HWND edit = new_richedit( NULL );
8582 const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8583 "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8584 "\\f0\\fs25 Second\\par"
8585 "{\\f0\\fs26 Third}\\par"
8586 "{\\f0\\fs22 Fourth}\\par}}}";
8587 EDITSTREAM es;
8588 CHARFORMAT2W cf;
8589 int i, num, expect_height;
8590
8591 es.dwCookie = (DWORD_PTR)&rtf;
8592 es.dwError = 0;
8593 es.pfnCallback = test_EM_STREAMIN_esCallback;
8594 num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8595 ok( num == 25, "got %d\n", num );
8596
8597 for (i = 0; i <= num; i++)
8598 {
8599 SendMessageW( edit, EM_SETSEL, i, i + 1 );
8600 cf.cbSize = sizeof(cf);
8601 cf.dwMask = CFM_SIZE;
8602 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8603 ok( cf.dwMask & CFM_SIZE, "%d: got %08x\n", i, cf.dwMask );
8604 if (i < 6) expect_height = 160;
8605 else if (i < 13) expect_height = 250;
8606 else if (i < 18) expect_height = 260;
8607 else if (i == 18 || i == 25) expect_height = 250;
8608 else expect_height = 220;
8609 ok( cf.yHeight == expect_height, "%d: got %d\n", i, cf.yHeight );
8610 }
8611
8612 DestroyWindow( edit );
8613 }
8614
8615 static void test_para_numbering(void)
8616 {
8617 HWND edit = new_richeditW( NULL );
8618 const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8619 "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8620 "\\fs20\\fi200\\li360\\f0 First\\par"
8621 "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
8622 "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
8623 const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
8624 EDITSTREAM es;
8625 WCHAR buf[80];
8626 LRESULT result;
8627 PARAFORMAT2 fmt, fmt2;
8628 GETTEXTEX get_text;
8629 CHARFORMAT2W cf;
8630
8631 get_text.cb = sizeof(buf);
8632 get_text.flags = GT_RAWTEXT;
8633 get_text.codepage = 1200;
8634 get_text.lpDefaultChar = NULL;
8635 get_text.lpUsedDefChar = NULL;
8636
8637 es.dwCookie = (DWORD_PTR)&numbers;
8638 es.dwError = 0;
8639 es.pfnCallback = test_EM_STREAMIN_esCallback;
8640 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8641 ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8642
8643 result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
8644 ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8645 ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
8646
8647 SendMessageW( edit, EM_SETSEL, 1, 1 );
8648 memset( &fmt, 0, sizeof(fmt) );
8649 fmt.cbSize = sizeof(fmt);
8650 fmt.dwMask = PFM_ALL2;
8651 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8652 ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
8653 ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
8654 ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
8655 ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
8656 ok( fmt.dxStartIndent == 560, "got %d\n", fmt.dxStartIndent );
8657 ok( fmt.dxOffset == -200, "got %d\n", fmt.dxOffset );
8658
8659 /* Second para should have identical fmt */
8660 SendMessageW( edit, EM_SETSEL, 10, 10 );
8661 memset( &fmt2, 0, sizeof(fmt2) );
8662 fmt2.cbSize = sizeof(fmt2);
8663 fmt2.dwMask = PFM_ALL2;
8664 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
8665 ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
8666
8667 /* Check the eop heights - this determines the label height */
8668 SendMessageW( edit, EM_SETSEL, 12, 13 );
8669 cf.cbSize = sizeof(cf);
8670 cf.dwMask = CFM_SIZE;
8671 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8672 ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8673
8674 SendMessageW( edit, EM_SETSEL, 18, 19 );
8675 cf.cbSize = sizeof(cf);
8676 cf.dwMask = CFM_SIZE;
8677 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8678 ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8679
8680 DestroyWindow( edit );
8681 }
8682
8683 static void test_window_classes(void)
8684 {
8685 static const struct
8686 {
8687 const char *class;
8688 BOOL success;
8689 } test[] =
8690 {
8691 { "RichEdit", FALSE },
8692 { "RichEdit20A", TRUE },
8693 { "RichEdit20W", TRUE },
8694 { "RichEdit50A", FALSE },
8695 { "RichEdit50W", FALSE }
8696 };
8697 int i;
8698 HWND hwnd;
8699
8700 for (i = 0; i < sizeof(test)/sizeof(test[0]); i++)
8701 {
8702 SetLastError(0xdeadbeef);
8703 hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
8704 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
8705 ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
8706 test[i].class, test[i].success ? "succeed" : "fail");
8707 if (!hwnd)
8708 todo_wine
8709 ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %d\n", GetLastError());
8710 else
8711 DestroyWindow(hwnd);
8712 }
8713 }
8714
8715 START_TEST( editor )
8716 {
8717 BOOL ret;
8718 /* Must explicitly LoadLibrary(). The test has no references to functions in
8719 * RICHED20.DLL, so the linker doesn't actually link to it. */
8720 hmoduleRichEdit = LoadLibraryA("riched20.dll");
8721 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8722 is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
8723
8724 test_window_classes();
8725 test_WM_CHAR();
8726 test_EM_FINDTEXT(FALSE);
8727 test_EM_FINDTEXT(TRUE);
8728 test_EM_GETLINE();
8729 test_EM_POSFROMCHAR();
8730 test_EM_SCROLLCARET();
8731 test_EM_SCROLL();
8732 test_scrollbar_visibility();
8733 test_WM_SETTEXT();
8734 test_EM_LINELENGTH();
8735 test_EM_SETCHARFORMAT();
8736 test_EM_SETTEXTMODE();
8737 test_TM_PLAINTEXT();
8738 test_EM_SETOPTIONS();
8739 test_WM_GETTEXT();
8740 test_EM_GETTEXTRANGE();
8741 test_EM_GETSELTEXT();
8742 test_EM_SETUNDOLIMIT();
8743 test_ES_PASSWORD();
8744 test_EM_SETTEXTEX();
8745 test_EM_LIMITTEXT();
8746 test_EM_EXLIMITTEXT();
8747 test_EM_GETLIMITTEXT();
8748 test_WM_SETFONT();
8749 test_EM_GETMODIFY();
8750 test_EM_SETSEL();
8751 test_EM_EXSETSEL();
8752 test_WM_PASTE();
8753 test_EM_STREAMIN();
8754 test_EM_STREAMOUT();
8755 test_EM_STREAMOUT_FONTTBL();
8756 test_EM_StreamIn_Undo();
8757 test_EM_FORMATRANGE();
8758 test_unicode_conversions();
8759 test_EM_GETTEXTLENGTHEX();
8760 test_WM_GETTEXTLENGTH();
8761 test_EM_REPLACESEL(1);
8762 test_EM_REPLACESEL(0);
8763 test_WM_NOTIFY();
8764 test_EN_LINK();
8765 test_EM_AUTOURLDETECT();
8766 test_eventMask();
8767 test_undo_coalescing();
8768 test_word_movement();
8769 test_EM_CHARFROMPOS();
8770 test_SETPARAFORMAT();
8771 test_word_wrap();
8772 test_autoscroll();
8773 test_format_rect();
8774 test_WM_GETDLGCODE();
8775 test_zoom();
8776 test_dialogmode();
8777 test_EM_FINDWORDBREAK_W();
8778 test_EM_FINDWORDBREAK_A();
8779 test_enter();
8780 test_WM_CREATE();
8781 test_reset_default_para_fmt();
8782 test_EM_SETREADONLY();
8783 test_EM_SETFONTSIZE();
8784 test_alignment_style();
8785 test_rtf_specials();
8786 test_background();
8787 test_eop_char_fmt();
8788 test_para_numbering();
8789
8790 /* Set the environment variable WINETEST_RICHED20 to keep windows
8791 * responsive and open for 30 seconds. This is useful for debugging.
8792 */
8793 if (getenv( "WINETEST_RICHED20" )) {
8794 keep_responsive(30);
8795 }
8796
8797 OleFlushClipboard();
8798 ret = FreeLibrary(hmoduleRichEdit);
8799 ok(ret, "error: %d\n", (int) GetLastError());
8800 }