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