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 #define COBJMACROS
24
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <wingdi.h>
31 #include <winuser.h>
32 #include <winnls.h>
33 #include <ole2.h>
34 #include <richedit.h>
35 #include <richole.h>
36 #include <commdlg.h>
37 #include <time.h>
38 #include <wine/test.h>
39
40 #define ID_RICHEDITTESTDBUTTON 0x123
41
42 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
43
44 #define ok_w3(format, szString1, szString2, szString3) \
45 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
46 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
47 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
48 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
49 format, string1, string2, string3);
50
51 static HMODULE hmoduleRichEdit;
52 static BOOL is_lang_japanese;
53
new_window(LPCSTR lpClassName,DWORD dwStyle,HWND parent)54 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
55 HWND hwnd;
56 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
57 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
58 hmoduleRichEdit, NULL);
59 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60 return hwnd;
61 }
62
new_windowW(LPCWSTR lpClassName,DWORD dwStyle,HWND parent)63 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
64 HWND hwnd;
65 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
66 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
67 hmoduleRichEdit, NULL);
68 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
69 return hwnd;
70 }
71
new_richedit(HWND parent)72 static HWND new_richedit(HWND parent) {
73 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
74 }
75
new_richedit_with_style(HWND parent,DWORD style)76 static HWND new_richedit_with_style(HWND parent, DWORD style) {
77 return new_window(RICHEDIT_CLASS20A, style, parent);
78 }
79
new_richeditW(HWND parent)80 static HWND new_richeditW(HWND parent) {
81 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
82 }
83
84 /* Keeps the window reponsive for the deley_time in seconds.
85 * This is useful for debugging a test to see what is happening. */
keep_responsive(time_t delay_time)86 static void keep_responsive(time_t delay_time)
87 {
88 MSG msg;
89 time_t end;
90
91 /* The message pump uses PeekMessage() to empty the queue and then
92 * sleeps for 50ms before retrying the queue. */
93 end = time(NULL) + delay_time;
94 while (time(NULL) < end) {
95 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
96 TranslateMessage(&msg);
97 DispatchMessageA(&msg);
98 } else {
99 Sleep(50);
100 }
101 }
102 }
103
simulate_typing_characters(HWND hwnd,const char * szChars)104 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 {
106 int ret;
107
108 while (*szChars != '\0') {
109 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
110 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
111 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
112 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
113 szChars++;
114 }
115 }
116
hold_key(int vk)117 static BOOL hold_key(int vk)
118 {
119 BYTE key_state[256];
120 BOOL result;
121
122 result = GetKeyboardState(key_state);
123 ok(result, "GetKeyboardState failed.\n");
124 if (!result) return FALSE;
125 key_state[vk] |= 0x80;
126 result = SetKeyboardState(key_state);
127 ok(result, "SetKeyboardState failed.\n");
128 return result != 0;
129 }
130
release_key(int vk)131 static BOOL release_key(int vk)
132 {
133 BYTE key_state[256];
134 BOOL result;
135
136 result = GetKeyboardState(key_state);
137 ok(result, "GetKeyboardState failed.\n");
138 if (!result) return FALSE;
139 key_state[vk] &= ~0x80;
140 result = SetKeyboardState(key_state);
141 ok(result, "SetKeyboardState failed.\n");
142 return result != 0;
143 }
144
145 static const char haystack[] = "WINEWine wineWine wine WineWine";
146 /* ^0 ^10 ^20 ^30 */
147
148 struct find_s {
149 int start;
150 int end;
151 const char *needle;
152 int flags;
153 int expected_loc;
154 };
155
156
157 static struct find_s find_tests[] = {
158 /* Find in empty text */
159 {0, -1, "foo", FR_DOWN, -1},
160 {0, -1, "foo", 0, -1},
161 {0, -1, "", FR_DOWN, -1},
162 {20, 5, "foo", FR_DOWN, -1},
163 {5, 20, "foo", FR_DOWN, -1}
164 };
165
166 static struct find_s find_tests2[] = {
167 /* No-result find */
168 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
169 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170
171 /* Subsequent finds */
172 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
173 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
174 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
175 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176
177 /* Find backwards */
178 {19, 20, "Wine", FR_MATCHCASE, 13},
179 {10, 20, "Wine", FR_MATCHCASE, 4},
180 {20, 10, "Wine", FR_MATCHCASE, 13},
181
182 /* Case-insensitive */
183 {1, 31, "wInE", FR_DOWN, 4},
184 {1, 31, "Wine", FR_DOWN, 4},
185
186 /* High-to-low ranges */
187 {20, 5, "Wine", FR_DOWN, -1},
188 {2, 1, "Wine", FR_DOWN, -1},
189 {30, 29, "Wine", FR_DOWN, -1},
190 {20, 5, "Wine", 0, 13},
191
192 /* Find nothing */
193 {5, 10, "", FR_DOWN, -1},
194 {10, 5, "", FR_DOWN, -1},
195 {0, -1, "", FR_DOWN, -1},
196 {10, 5, "", 0, -1},
197
198 /* Whole-word search */
199 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
200 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
201 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
202 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
203 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
204 {11, -1, "winewine", FR_WHOLEWORD, 0},
205 {31, -1, "winewine", FR_WHOLEWORD, 23},
206
207 /* Bad ranges */
208 {5, 200, "XXX", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-20, 20, "Wine", FR_DOWN, -1},
211 {-15, -20, "Wine", FR_DOWN, -1},
212 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213
214 /* Check the case noted in bug 4479 where matches at end aren't recognized */
215 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
216 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220
221 /* The backwards case of bug 4479; bounds look right
222 * Fails because backward find is wrong */
223 {19, 20, "WINE", FR_MATCHCASE, 0},
224 {0, 20, "WINE", FR_MATCHCASE, -1},
225
226 {0, -1, "wineWine wine", 0, -1},
227 };
228
atowstr(const char * str)229 static WCHAR *atowstr(const char *str)
230 {
231 WCHAR *ret;
232 DWORD len;
233 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
234 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
235 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
236 return ret;
237 }
238
check_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * f,int id,BOOL unicode)239 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
240 {
241 int findloc;
242
243 if(unicode){
244 FINDTEXTW ftw;
245 memset(&ftw, 0, sizeof(ftw));
246 ftw.chrg.cpMin = f->start;
247 ftw.chrg.cpMax = f->end;
248 ftw.lpstrText = atowstr(f->needle);
249
250 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
251 ok(findloc == f->expected_loc,
252 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
253 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254
255 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
256 ok(findloc == f->expected_loc,
257 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
258 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
259
260 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
261 }else{
262 FINDTEXTA fta;
263 memset(&fta, 0, sizeof(fta));
264 fta.chrg.cpMin = f->start;
265 fta.chrg.cpMax = f->end;
266 fta.lpstrText = f->needle;
267
268 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
269 ok(findloc == f->expected_loc,
270 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
271 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
272 }
273 }
274
check_EM_FINDTEXTEX(HWND hwnd,const char * name,struct find_s * f,int id,BOOL unicode)275 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
276 int id, BOOL unicode)
277 {
278 int findloc;
279 int expected_end_loc;
280
281 if(unicode){
282 FINDTEXTEXW ftw;
283 memset(&ftw, 0, sizeof(ftw));
284 ftw.chrg.cpMin = f->start;
285 ftw.chrg.cpMax = f->end;
286 ftw.lpstrText = atowstr(f->needle);
287 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
288 ok(findloc == f->expected_loc,
289 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
290 name, id, f->needle, f->start, f->end, f->flags, findloc);
291 ok(ftw.chrgText.cpMin == f->expected_loc,
292 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
293 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
294 expected_end_loc = ((f->expected_loc == -1) ? -1
295 : f->expected_loc + strlen(f->needle));
296 ok(ftw.chrgText.cpMax == expected_end_loc,
297 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
298 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
299 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
300 }else{
301 FINDTEXTEXA fta;
302 memset(&fta, 0, sizeof(fta));
303 fta.chrg.cpMin = f->start;
304 fta.chrg.cpMax = f->end;
305 fta.lpstrText = f->needle;
306 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
307 ok(findloc == f->expected_loc,
308 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
309 name, id, f->needle, f->start, f->end, f->flags, findloc);
310 ok(fta.chrgText.cpMin == f->expected_loc,
311 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
312 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
313 expected_end_loc = ((f->expected_loc == -1) ? -1
314 : f->expected_loc + strlen(f->needle));
315 ok(fta.chrgText.cpMax == expected_end_loc,
316 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
317 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
318 }
319 }
320
run_tests_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * find,int num_tests,BOOL unicode)321 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
322 int num_tests, BOOL unicode)
323 {
324 int i;
325
326 for (i = 0; i < num_tests; i++) {
327 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
328 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
329 }
330 }
331
test_EM_FINDTEXT(BOOL unicode)332 static void test_EM_FINDTEXT(BOOL unicode)
333 {
334 HWND hwndRichEdit;
335 CHARFORMAT2A cf2;
336
337 if(unicode)
338 hwndRichEdit = new_richeditW(NULL);
339 else
340 hwndRichEdit = new_richedit(NULL);
341
342 /* Empty rich edit control */
343 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests), unicode);
344
345 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
346
347 /* Haystack text */
348 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2), unicode);
349
350 /* Setting a format on an arbitrary range should have no effect in search
351 results. This tests correct offset reporting across runs. */
352 cf2.cbSize = sizeof(CHARFORMAT2A);
353 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
354 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
355 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
356 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
357 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
358
359 /* Haystack text, again */
360 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
361
362 /* Yet another range */
363 cf2.dwMask = CFM_BOLD | cf2.dwMask;
364 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
365 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
366 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
367
368 /* Haystack text, again */
369 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
370
371 DestroyWindow(hwndRichEdit);
372 }
373
374 static const struct getline_s {
375 int line;
376 size_t buffer_len;
377 const char *text;
378 } gl[] = {
379 {0, 10, "foo bar\r"},
380 {1, 10, "\r"},
381 {2, 10, "bar\r"},
382 {3, 10, "\r"},
383
384 /* Buffer smaller than line length */
385 {0, 2, "foo bar\r"},
386 {0, 1, "foo bar\r"},
387 {0, 0, "foo bar\r"}
388 };
389
test_EM_GETLINE(void)390 static void test_EM_GETLINE(void)
391 {
392 int i;
393 HWND hwndRichEdit = new_richedit(NULL);
394 static const int nBuf = 1024;
395 char dest[1024], origdest[1024];
396 const char text[] = "foo bar\n"
397 "\n"
398 "bar\n";
399
400 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
401
402 memset(origdest, 0xBB, nBuf);
403 for (i = 0; i < ARRAY_SIZE(gl); i++)
404 {
405 int nCopied;
406 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
407 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
408 memset(dest, 0xBB, nBuf);
409 *(WORD *) dest = gl[i].buffer_len;
410
411 /* EM_GETLINE appends a "\r\0" to the end of the line
412 * nCopied counts up to and including the '\r' */
413 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
414 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
415 expected_nCopied);
416 /* two special cases since a parameter is passed via dest */
417 if (gl[i].buffer_len == 0)
418 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
419 "buffer_len=0\n");
420 else if (gl[i].buffer_len == 1)
421 ok(dest[0] == gl[i].text[0] && !dest[1] &&
422 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
423 else
424 {
425 /* Prepare hex strings of buffers to dump on failure. */
426 char expectedbuf[1024];
427 char resultbuf[1024];
428 int j;
429 resultbuf[0] = '\0';
430 for (j = 0; j < 32; j++)
431 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
432 expectedbuf[0] = '\0';
433 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
434 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
435 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
436 sprintf(expectedbuf+strlen(expectedbuf), "??");
437 for (; j < 32; j++) /* Bytes after declared buffer size */
438 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
439
440 /* Test the part of the buffer that is expected to be written according
441 * to the MSDN documentation fo EM_GETLINE, which does not state that
442 * a NULL terminating character will be added unless no text is copied.
443 *
444 * Windows NT does not append a NULL terminating character, but
445 * Windows 2000 and up do append a NULL terminating character if there
446 * is space in the buffer. The test will ignore this difference. */
447 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
448 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
449 i, expected_bytes_written, expectedbuf, resultbuf);
450 /* Test the part of the buffer after the declared length to make sure
451 * there are no buffer overruns. */
452 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
453 nBuf - gl[i].buffer_len),
454 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
455 i, expected_bytes_written, expectedbuf, resultbuf);
456 }
457 }
458
459 DestroyWindow(hwndRichEdit);
460 }
461
test_EM_LINELENGTH(void)462 static void test_EM_LINELENGTH(void)
463 {
464 HWND hwndRichEdit = new_richedit(NULL);
465 const char * text =
466 "richedit1\r"
467 "richedit1\n"
468 "richedit1\r\n"
469 "richedit1";
470 int offset_test[10][2] = {
471 {0, 9},
472 {5, 9},
473 {10, 9},
474 {15, 9},
475 {20, 9},
476 {25, 9},
477 {30, 9},
478 {35, 9},
479 {40, 0},
480 {45, 0},
481 };
482 int i;
483 LRESULT result;
484
485 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
486
487 for (i = 0; i < 10; i++) {
488 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
489 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
490 offset_test[i][0], result, offset_test[i][1]);
491 }
492
493 /* Test with multibyte character */
494 if (!is_lang_japanese)
495 skip("Skip multibyte character tests on non-Japanese platform\n");
496 else
497 {
498 const char *text1 =
499 "wine\n"
500 "richedit\x8e\xf0\n"
501 "wine";
502 int offset_test1[3][2] = {
503 {0, 4}, /* Line 1: |wine\n */
504 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
505 {15, 4}, /* Line 3: |wine */
506 };
507 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
508 for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
509 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
510 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
511 offset_test1[i][0], result, offset_test1[i][1]);
512 }
513 }
514
515 DestroyWindow(hwndRichEdit);
516 }
517
get_scroll_pos_y(HWND hwnd)518 static int get_scroll_pos_y(HWND hwnd)
519 {
520 POINT p = {-1, -1};
521 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
522 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
523 return p.y;
524 }
525
move_cursor(HWND hwnd,LONG charindex)526 static void move_cursor(HWND hwnd, LONG charindex)
527 {
528 CHARRANGE cr;
529 cr.cpMax = charindex;
530 cr.cpMin = charindex;
531 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
532 }
533
line_scroll(HWND hwnd,int amount)534 static void line_scroll(HWND hwnd, int amount)
535 {
536 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
537 }
538
test_EM_SCROLLCARET(void)539 static void test_EM_SCROLLCARET(void)
540 {
541 int prevY, curY;
542 const char text[] = "aa\n"
543 "this is a long line of text that should be longer than the "
544 "control's width\n"
545 "cc\n"
546 "dd\n"
547 "ee\n"
548 "ff\n"
549 "gg\n"
550 "hh\n";
551 /* The richedit window height needs to be large enough vertically to fit in
552 * more than two lines of text, so the new_richedit function can't be used
553 * since a height of 60 was not large enough on some systems.
554 */
555 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
556 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
557 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
558 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
559
560 /* Can't verify this */
561 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
562
563 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
564
565 /* Caret above visible window */
566 line_scroll(hwndRichEdit, 3);
567 prevY = get_scroll_pos_y(hwndRichEdit);
568 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
569 curY = get_scroll_pos_y(hwndRichEdit);
570 ok(prevY != curY, "%d == %d\n", prevY, curY);
571
572 /* Caret below visible window */
573 move_cursor(hwndRichEdit, sizeof(text) - 1);
574 line_scroll(hwndRichEdit, -3);
575 prevY = get_scroll_pos_y(hwndRichEdit);
576 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
577 curY = get_scroll_pos_y(hwndRichEdit);
578 ok(prevY != curY, "%d == %d\n", prevY, curY);
579
580 /* Caret in visible window */
581 move_cursor(hwndRichEdit, sizeof(text) - 2);
582 prevY = get_scroll_pos_y(hwndRichEdit);
583 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
584 curY = get_scroll_pos_y(hwndRichEdit);
585 ok(prevY == curY, "%d != %d\n", prevY, curY);
586
587 /* Caret still in visible window */
588 line_scroll(hwndRichEdit, -1);
589 prevY = get_scroll_pos_y(hwndRichEdit);
590 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
591 curY = get_scroll_pos_y(hwndRichEdit);
592 ok(prevY == curY, "%d != %d\n", prevY, curY);
593
594 DestroyWindow(hwndRichEdit);
595 }
596
test_EM_POSFROMCHAR(void)597 static void test_EM_POSFROMCHAR(void)
598 {
599 HWND hwndRichEdit = new_richedit(NULL);
600 int i, expected;
601 LRESULT result;
602 unsigned int height = 0;
603 int xpos = 0;
604 POINTL pt;
605 LOCALESIGNATURE sig;
606 BOOL rtl;
607 PARAFORMAT2 fmt;
608 static const char text[] = "aa\n"
609 "this is a long line of text that should be longer than the "
610 "control's width\n"
611 "cc\n"
612 "dd\n"
613 "ee\n"
614 "ff\n"
615 "gg\n"
616 "hh\n";
617
618 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
619 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
620 (sig.lsUsb[3] & 0x08000000) != 0);
621
622 /* Fill the control to lines to ensure that most of them are offscreen */
623 for (i = 0; i < 50; i++)
624 {
625 /* Do not modify the string; it is exactly 16 characters long. */
626 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
627 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
628 }
629
630 /*
631 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
632 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
633 Richedit 3.0 accepts either of the above API conventions.
634 */
635
636 /* Testing Richedit 2.0 API format */
637
638 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
639 Since all lines are identical and drawn with the same font,
640 they should have the same height... right?
641 */
642 for (i = 0; i < 50; i++)
643 {
644 /* All the lines are 16 characters long */
645 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
646 if (i == 0)
647 {
648 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
649 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
650 xpos = LOWORD(result);
651 }
652 else if (i == 1)
653 {
654 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
655 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
656 height = HIWORD(result);
657 }
658 else
659 {
660 ros_skip_flaky
661 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
662 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
663 }
664 }
665
666 /* Testing position at end of text */
667 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
668 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
669 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
670
671 /* Testing position way past end of text */
672 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
673 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
674 expected = (rtl ? 8 : 1);
675 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
676
677 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
678 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
679 for (i = 0; i < 50; i++)
680 {
681 /* All the lines are 16 characters long */
682 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
683 ok((signed short)(HIWORD(result)) == (i - 1) * height,
684 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
685 (signed short)(HIWORD(result)), (i - 1) * height);
686 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
687 }
688
689 /* Testing position at end of text */
690 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
691 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
692 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
693
694 /* Testing position way past end of text */
695 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
696 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
697 expected = (rtl ? 8 : 1);
698 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
699
700 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
701 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
702 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
703
704 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
705 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
706 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
707 xpos = LOWORD(result);
708
709 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
710 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
711 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
712 ok((signed short)(LOWORD(result)) < xpos,
713 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
714 (signed short)(LOWORD(result)), xpos);
715 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
716
717 /* Test around end of text that doesn't end in a newline. */
718 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
719 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
721 ok(pt.x > 1, "pt.x = %d\n", pt.x);
722 xpos = pt.x;
723 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
725 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
726 xpos = (rtl ? pt.x + 7 : pt.x);
727 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
728 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
729 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
730
731 /* Try a negative position. */
732 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
733 ok(pt.x == 1, "pt.x = %d\n", pt.x);
734
735 /* test negative indentation */
736 SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
737 (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
738 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
739 ok(pt.x == 1, "pt.x = %d\n", pt.x);
740
741 fmt.cbSize = sizeof(fmt);
742 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
743 ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
744 ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
745 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
746
747 DestroyWindow(hwndRichEdit);
748 }
749
test_EM_SETCHARFORMAT(void)750 static void test_EM_SETCHARFORMAT(void)
751 {
752 HWND hwndRichEdit = new_richedit(NULL);
753 CHARFORMAT2A cf2;
754 CHARFORMAT2W cfW;
755 CHARFORMATA cf1a;
756 CHARFORMATW cf1w;
757 int rc = 0;
758 int tested_effects[] = {
759 CFE_BOLD,
760 CFE_ITALIC,
761 CFE_UNDERLINE,
762 CFE_STRIKEOUT,
763 CFE_PROTECTED,
764 CFE_LINK,
765 CFE_SUBSCRIPT,
766 CFE_SUPERSCRIPT,
767 0
768 };
769 int i;
770 CHARRANGE cr;
771 LOCALESIGNATURE sig;
772 BOOL rtl;
773 DWORD expect_effects;
774
775 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
776 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
777 (sig.lsUsb[3] & 0x08000000) != 0);
778
779 /* check charformat defaults */
780 memset(&cf2, 0, sizeof(CHARFORMAT2A));
781 cf2.cbSize = sizeof(CHARFORMAT2A);
782 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
783 ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
784 expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
785 if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
786 ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
787 ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
788 ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
789 ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
790 ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
791 ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
792 ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
793 ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
794
795 /* Invalid flags, CHARFORMAT2 structure blanked out */
796 memset(&cf2, 0, sizeof(cf2));
797 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
798 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
799
800 /* A valid flag, CHARFORMAT2 structure blanked out */
801 memset(&cf2, 0, sizeof(cf2));
802 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
803 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
804
805 /* A valid flag, CHARFORMAT2 structure blanked out */
806 memset(&cf2, 0, sizeof(cf2));
807 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
808 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
809
810 /* A valid flag, CHARFORMAT2 structure blanked out */
811 memset(&cf2, 0, sizeof(cf2));
812 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
813 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
814
815 /* A valid flag, CHARFORMAT2 structure blanked out */
816 memset(&cf2, 0, sizeof(cf2));
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
818 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
819
820 /* Invalid flags, CHARFORMAT2 structure minimally filled */
821 memset(&cf2, 0, sizeof(cf2));
822 cf2.cbSize = sizeof(CHARFORMAT2A);
823 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
824 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
825 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
826 ok(rc == FALSE, "Should not be able to undo here.\n");
827 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
828
829 /* A valid flag, CHARFORMAT2 structure minimally filled */
830 memset(&cf2, 0, sizeof(cf2));
831 cf2.cbSize = sizeof(CHARFORMAT2A);
832 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
833 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
835 ok(rc == FALSE, "Should not be able to undo here.\n");
836 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
837
838 /* A valid flag, CHARFORMAT2 structure minimally filled */
839 memset(&cf2, 0, sizeof(cf2));
840 cf2.cbSize = sizeof(CHARFORMAT2A);
841 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
842 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
843 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
844 ok(rc == FALSE, "Should not be able to undo here.\n");
845 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
846
847 /* A valid flag, CHARFORMAT2 structure minimally filled */
848 memset(&cf2, 0, sizeof(cf2));
849 cf2.cbSize = sizeof(CHARFORMAT2A);
850 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
851 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
852 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
853 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
854 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
855
856 /* A valid flag, CHARFORMAT2 structure minimally filled */
857 memset(&cf2, 0, sizeof(cf2));
858 cf2.cbSize = sizeof(CHARFORMAT2A);
859 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
860 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
861 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
862 ok(rc == TRUE, "Should not be able to undo here.\n");
863 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
864
865 cf2.cbSize = sizeof(CHARFORMAT2A);
866 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
867
868 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
869 cf2.cbSize = sizeof(CHARFORMAT2A);
870 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
871 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
872 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
873
874 /* wParam==0 is default char format, does not set modify */
875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
876 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
877 ok(rc == 0, "Text marked as modified, expected not modified!\n");
878 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
879 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
880 if (! rtl)
881 {
882 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
883 ok(rc == 0, "Text marked as modified, expected not modified!\n");
884 }
885 else
886 skip("RTL language found\n");
887
888 /* wParam==SCF_SELECTION sets modify if nonempty selection */
889 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
890 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
891 ok(rc == 0, "Text marked as modified, expected not modified!\n");
892 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
893 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
894 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
895 ok(rc == 0, "Text marked as modified, expected not modified!\n");
896
897 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
899 ok(rc == 0, "Text marked as modified, expected not modified!\n");
900 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
902 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
903 ok(rc == 0, "Text marked as modified, expected not modified!\n");
904 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
905 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
906 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
907 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
908 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
909
910 /* wParam==SCF_ALL sets modify regardless of whether text is present */
911 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
912 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
913 ok(rc == 0, "Text marked as modified, expected not modified!\n");
914 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
915 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
916 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
917 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
918
919 DestroyWindow(hwndRichEdit);
920
921 /* EM_GETCHARFORMAT tests */
922 for (i = 0; tested_effects[i]; i++)
923 {
924 hwndRichEdit = new_richedit(NULL);
925 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
926
927 /* Need to set a TrueType font to get consistent CFM_BOLD results */
928 memset(&cf2, 0, sizeof(CHARFORMAT2A));
929 cf2.cbSize = sizeof(CHARFORMAT2A);
930 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
931 cf2.dwEffects = 0;
932 strcpy(cf2.szFaceName, "Courier New");
933 cf2.wWeight = FW_DONTCARE;
934 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
935
936 memset(&cf2, 0, sizeof(CHARFORMAT2A));
937 cf2.cbSize = sizeof(CHARFORMAT2A);
938 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
939 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
940 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
941 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
942 ||
943 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
944 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
945 ok((cf2.dwEffects & tested_effects[i]) == 0,
946 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
947
948 memset(&cf2, 0, sizeof(CHARFORMAT2A));
949 cf2.cbSize = sizeof(CHARFORMAT2A);
950 cf2.dwMask = tested_effects[i];
951 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952 cf2.dwMask = CFM_SUPERSCRIPT;
953 cf2.dwEffects = tested_effects[i];
954 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
955 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
956
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
963 ||
964 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
967 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
968
969 memset(&cf2, 0, sizeof(CHARFORMAT2A));
970 cf2.cbSize = sizeof(CHARFORMAT2A);
971 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975 ||
976 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978 ok((cf2.dwEffects & tested_effects[i]) == 0,
979 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980
981 memset(&cf2, 0, sizeof(CHARFORMAT2A));
982 cf2.cbSize = sizeof(CHARFORMAT2A);
983 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
987 ||
988 (cf2.dwMask & tested_effects[i]) == 0),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990
991 DestroyWindow(hwndRichEdit);
992 }
993
994 for (i = 0; tested_effects[i]; i++)
995 {
996 hwndRichEdit = new_richedit(NULL);
997 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
998
999 /* Need to set a TrueType font to get consistent CFM_BOLD results */
1000 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1001 cf2.cbSize = sizeof(CHARFORMAT2A);
1002 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1003 cf2.dwEffects = 0;
1004 strcpy(cf2.szFaceName, "Courier New");
1005 cf2.wWeight = FW_DONTCARE;
1006 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1007
1008 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1009 cf2.cbSize = sizeof(CHARFORMAT2A);
1010 cf2.dwMask = tested_effects[i];
1011 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1012 cf2.dwMask = CFM_SUPERSCRIPT;
1013 cf2.dwEffects = tested_effects[i];
1014 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1015 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016
1017 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018 cf2.cbSize = sizeof(CHARFORMAT2A);
1019 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1020 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1023 ||
1024 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1025 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1026 ok((cf2.dwEffects & tested_effects[i]) == 0,
1027 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1028
1029 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1030 cf2.cbSize = sizeof(CHARFORMAT2A);
1031 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1032 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1033 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1034 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1035 ||
1036 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1037 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1038 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1039 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1040
1041 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1042 cf2.cbSize = sizeof(CHARFORMAT2A);
1043 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1044 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1045 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1046 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1047 ||
1048 (cf2.dwMask & tested_effects[i]) == 0),
1049 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1050 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1051 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1052
1053 DestroyWindow(hwndRichEdit);
1054 }
1055
1056 /* Effects applied on an empty selection should take effect when selection is
1057 replaced with text */
1058 hwndRichEdit = new_richedit(NULL);
1059 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1061
1062 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063 cf2.cbSize = sizeof(CHARFORMAT2A);
1064 cf2.dwMask = CFM_BOLD;
1065 cf2.dwEffects = CFE_BOLD;
1066 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067
1068 /* Selection is now nonempty */
1069 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1070
1071 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1072 cf2.cbSize = sizeof(CHARFORMAT2A);
1073 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1074 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1075
1076 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1077 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1078 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1079 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1080
1081
1082 /* Set two effects on an empty selection */
1083 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1084 /* first clear bold, italic */
1085 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1086 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1087 cf2.cbSize = sizeof(CHARFORMAT2A);
1088 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1089 cf2.dwEffects = 0;
1090 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1091
1092 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1093
1094 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1095 cf2.cbSize = sizeof(CHARFORMAT2A);
1096 cf2.dwMask = CFM_BOLD;
1097 cf2.dwEffects = CFE_BOLD;
1098 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099 cf2.dwMask = CFM_ITALIC;
1100 cf2.dwEffects = CFE_ITALIC;
1101 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1102
1103 /* Selection is now nonempty */
1104 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1105
1106 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1107 cf2.cbSize = sizeof(CHARFORMAT2A);
1108 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1109 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1110
1111 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1112 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1113 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1114 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1115
1116 /* Setting the (empty) selection to exactly the same place as before should
1117 NOT clear the insertion style! */
1118 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1119 /* first clear bold, italic */
1120 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1121 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1122 cf2.cbSize = sizeof(CHARFORMAT2A);
1123 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1124 cf2.dwEffects = 0;
1125 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1126
1127 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1128
1129 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130 cf2.cbSize = sizeof(CHARFORMAT2A);
1131 cf2.dwMask = CFM_BOLD;
1132 cf2.dwEffects = CFE_BOLD;
1133 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1134
1135 /* Empty selection in same place, insert style should NOT be forgotten here. */
1136 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1137
1138 /* Selection is now nonempty */
1139 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1140
1141 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1142 cf2.cbSize = sizeof(CHARFORMAT2A);
1143 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1144 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1145
1146 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1147 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1148 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1149 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1150
1151 /* Moving the selection will clear the insertion style */
1152 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1153 /* first clear bold, italic */
1154 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1155 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1156 cf2.cbSize = sizeof(CHARFORMAT2A);
1157 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1158 cf2.dwEffects = 0;
1159 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1160
1161 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1162
1163 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1164 cf2.cbSize = sizeof(CHARFORMAT2A);
1165 cf2.dwMask = CFM_BOLD;
1166 cf2.dwEffects = CFE_BOLD;
1167 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1168
1169 /* Move selection and then put it back, insert style should be forgotten here. */
1170 SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1171 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1172
1173 /* Selection is now nonempty */
1174 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1175
1176 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1177 cf2.cbSize = sizeof(CHARFORMAT2A);
1178 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1179 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1180
1181 ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1182 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1183 ok((cf2.dwEffects & CFE_BOLD) == 0,
1184 "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1185
1186 /* Ditto with EM_EXSETSEL */
1187 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1188 /* first clear bold, italic */
1189 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1190 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1191 cf2.cbSize = sizeof(CHARFORMAT2A);
1192 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1193 cf2.dwEffects = 0;
1194 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1195
1196 cr.cpMin = 2; cr.cpMax = 2;
1197 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1198
1199 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1200 cf2.cbSize = sizeof(CHARFORMAT2A);
1201 cf2.dwMask = CFM_BOLD;
1202 cf2.dwEffects = CFE_BOLD;
1203 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1204
1205 /* Empty selection in same place, insert style should NOT be forgotten here. */
1206 cr.cpMin = 2; cr.cpMax = 2;
1207 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1208
1209 /* Selection is now nonempty */
1210 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1211
1212 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1213 cf2.cbSize = sizeof(CHARFORMAT2A);
1214 cr.cpMin = 2; cr.cpMax = 6;
1215 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1216 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1217
1218 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1219 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1220 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1221 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1222
1223 /* show that wWeight is at the correct offset in CHARFORMAT2A */
1224 memset(&cf2, 0, sizeof(cf2));
1225 cf2.cbSize = sizeof(cf2);
1226 cf2.dwMask = CFM_WEIGHT;
1227 cf2.wWeight = 100;
1228 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1229 memset(&cf2, 0, sizeof(cf2));
1230 cf2.cbSize = sizeof(cf2);
1231 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1232 ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1233
1234 memset(&cf2, 0, sizeof(cf2));
1235 cf2.cbSize = sizeof(cf2);
1236 cf2.dwMask = CFM_SPACING;
1237 cf2.sSpacing = 10;
1238 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1239 memset(&cf2, 0, sizeof(cf2));
1240 cf2.cbSize = sizeof(cf2);
1241 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1242 ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1243
1244 /* show that wWeight is at the correct offset in CHARFORMAT2W */
1245 memset(&cfW, 0, sizeof(cfW));
1246 cfW.cbSize = sizeof(cfW);
1247 cfW.dwMask = CFM_WEIGHT;
1248 cfW.wWeight = 100;
1249 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1250 memset(&cfW, 0, sizeof(cfW));
1251 cfW.cbSize = sizeof(cfW);
1252 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1253 ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1254
1255 memset(&cfW, 0, sizeof(cfW));
1256 cfW.cbSize = sizeof(cfW);
1257 cfW.dwMask = CFM_SPACING;
1258 cfW.sSpacing = 10;
1259 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1260 memset(&cfW, 0, sizeof(cfW));
1261 cfW.cbSize = sizeof(cfW);
1262 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1263 ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1264
1265 /* test CFE_UNDERLINE and bUnderlineType interaction */
1266 /* clear bold, italic */
1267 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1268 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1269 cf2.cbSize = sizeof(CHARFORMAT2A);
1270 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1271 cf2.dwEffects = 0;
1272 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273
1274 /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1275 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1276 cf2.cbSize = sizeof(CHARFORMAT2A);
1277 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1278 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1279 "got %08x\n", cf2.dwMask);
1280 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1281 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1282
1283 /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1284 cf2.dwMask = CFM_UNDERLINETYPE;
1285 cf2.bUnderlineType = CFU_UNDERLINE;
1286 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1287 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1288 cf2.cbSize = sizeof(CHARFORMAT2A);
1289 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1290 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1291 "got %08x\n", cf2.dwMask);
1292 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1293 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1294
1295 /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1296 cf2.dwMask = CFM_UNDERLINETYPE;
1297 cf2.bUnderlineType = CFU_UNDERLINENONE;
1298 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1299 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1300 cf2.cbSize = sizeof(CHARFORMAT2A);
1301 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1302 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1303 "got %08x\n", cf2.dwMask);
1304 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1305 ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1306
1307 /* another underline type also sets CFE_UNDERLINE */
1308 cf2.dwMask = CFM_UNDERLINETYPE;
1309 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1310 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1311 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1312 cf2.cbSize = sizeof(CHARFORMAT2A);
1313 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1314 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1315 "got %08x\n", cf2.dwMask);
1316 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1317 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1318
1319 /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1320 cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1321 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1322 cf2.dwEffects = 0;
1323 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1324 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1325 cf2.cbSize = sizeof(CHARFORMAT2A);
1326 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1327 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1328 "got %08x\n", cf2.dwMask);
1329 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1330 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1331
1332 /* And turing it back on again by just setting CFE_UNDERLINE */
1333 cf2.dwMask = CFM_UNDERLINE;
1334 cf2.dwEffects = CFE_UNDERLINE;
1335 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1336 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1337 cf2.cbSize = sizeof(CHARFORMAT2A);
1338 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1339 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1340 "got %08x\n", cf2.dwMask);
1341 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1342 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1343
1344 /* Check setting CFM_ALL2/CFM_EFFECTS2 in CHARFORMAT(A/W). */
1345 memset(&cf1a, 0, sizeof(CHARFORMATA));
1346 memset(&cf1w, 0, sizeof(CHARFORMATW));
1347 cf1a.cbSize = sizeof(CHARFORMATA);
1348 cf1w.cbSize = sizeof(CHARFORMATW);
1349 cf1a.dwMask = cf1w.dwMask = CFM_ALL2;
1350 cf1a.dwEffects = cf1w.dwEffects = CFM_EFFECTS2;
1351 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1352 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1353 /* flags only valid for CHARFORMAT2 should be masked out */
1354 ok((cf1a.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1355 ok((cf1a.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1356 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1357 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1358 ok((cf1w.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1359 ok((cf1w.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1360
1361 DestroyWindow(hwndRichEdit);
1362 }
1363
test_EM_SETTEXTMODE(void)1364 static void test_EM_SETTEXTMODE(void)
1365 {
1366 HWND hwndRichEdit = new_richedit(NULL);
1367 CHARFORMAT2A cf2, cf2test;
1368 CHARRANGE cr;
1369 int rc = 0;
1370
1371 /*Attempt to use mutually exclusive modes*/
1372 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1373 ok(rc == E_INVALIDARG,
1374 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1375
1376 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1377 /*Insert text into the control*/
1378
1379 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1380
1381 /*Attempt to change the control to plain text mode*/
1382 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1383 ok(rc == E_UNEXPECTED,
1384 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1385
1386 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1387 If rich text is pasted, it should have the same formatting as the rest
1388 of the text in the control*/
1389
1390 /*Italicize the text
1391 *NOTE: If the default text was already italicized, the test will simply
1392 reverse; in other words, it will copy a regular "wine" into a plain
1393 text window that uses an italicized format*/
1394 cf2.cbSize = sizeof(CHARFORMAT2A);
1395 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1396
1397 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1398 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1399
1400 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1401 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1402
1403 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1404 however, SCF_ALL has been implemented*/
1405 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1406 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1407
1408 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1409 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1410
1411 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1412
1413 /*Select the string "wine"*/
1414 cr.cpMin = 0;
1415 cr.cpMax = 4;
1416 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1417
1418 /*Copy the italicized "wine" to the clipboard*/
1419 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1420
1421 /*Reset the formatting to default*/
1422 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1423 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1424 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1425
1426 /*Clear the text in the control*/
1427 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1428
1429 /*Switch to Plain Text Mode*/
1430 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1431 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1432
1433 /*Input "wine" again in normal format*/
1434 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1435
1436 /*Paste the italicized "wine" into the control*/
1437 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1438
1439 /*Select a character from the first "wine" string*/
1440 cr.cpMin = 2;
1441 cr.cpMax = 3;
1442 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1443
1444 /*Retrieve its formatting*/
1445 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1446
1447 /*Select a character from the second "wine" string*/
1448 cr.cpMin = 5;
1449 cr.cpMax = 6;
1450 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1451
1452 /*Retrieve its formatting*/
1453 cf2test.cbSize = sizeof(CHARFORMAT2A);
1454 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1455
1456 /*Compare the two formattings*/
1457 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1458 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1459 cf2.dwEffects, cf2test.dwEffects);
1460 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1461 printing "wine" in the current format(normal)
1462 pasting "wine" from the clipboard(italicized)
1463 comparing the two formats(should differ)*/
1464
1465 /*Attempt to switch with text in control*/
1466 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1467 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1468
1469 /*Clear control*/
1470 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1471
1472 /*Switch into Rich Text mode*/
1473 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1474 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1475
1476 /*Print "wine" in normal formatting into the control*/
1477 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1478
1479 /*Paste italicized "wine" into the control*/
1480 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1481
1482 /*Select text from the first "wine" string*/
1483 cr.cpMin = 1;
1484 cr.cpMax = 3;
1485 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1486
1487 /*Retrieve its formatting*/
1488 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1489
1490 /*Select text from the second "wine" string*/
1491 cr.cpMin = 6;
1492 cr.cpMax = 7;
1493 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1494
1495 /*Retrieve its formatting*/
1496 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1497
1498 /*Test that the two formattings are not the same*/
1499 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1500 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1501 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1502
1503 DestroyWindow(hwndRichEdit);
1504 }
1505
test_SETPARAFORMAT(void)1506 static void test_SETPARAFORMAT(void)
1507 {
1508 HWND hwndRichEdit = new_richedit(NULL);
1509 PARAFORMAT2 fmt;
1510 HRESULT ret;
1511 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1512 fmt.cbSize = sizeof(PARAFORMAT2);
1513 fmt.dwMask = PFM_ALIGNMENT;
1514 fmt.wAlignment = PFA_LEFT;
1515
1516 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1517 ok(ret != 0, "expected non-zero got %d\n", ret);
1518
1519 fmt.cbSize = sizeof(PARAFORMAT2);
1520 fmt.dwMask = -1;
1521 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1522 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1523 * between richedit different native builds of riched20.dll
1524 * used on different Windows versions. */
1525 ret &= ~PFM_TABLEROWDELIMITER;
1526 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1527
1528 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1529 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1530
1531 /* Test some other paraformat field defaults */
1532 ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1533 ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1534 ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1535 ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1536
1537 DestroyWindow(hwndRichEdit);
1538 }
1539
test_TM_PLAINTEXT(void)1540 static void test_TM_PLAINTEXT(void)
1541 {
1542 /*Tests plain text properties*/
1543
1544 HWND hwndRichEdit = new_richedit(NULL);
1545 CHARFORMAT2A cf2, cf2test;
1546 CHARRANGE cr;
1547 int rc = 0;
1548
1549 /*Switch to plain text mode*/
1550
1551 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1552 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1553
1554 /*Fill control with text*/
1555
1556 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1557
1558 /*Select some text and bold it*/
1559
1560 cr.cpMin = 10;
1561 cr.cpMax = 20;
1562 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1563 cf2.cbSize = sizeof(CHARFORMAT2A);
1564 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1565
1566 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1567 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1568
1569 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1570 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1571
1572 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1573 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1574
1575 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1576 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1577
1578 /*Get the formatting of those characters*/
1579
1580 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1581
1582 /*Get the formatting of some other characters*/
1583 cf2test.cbSize = sizeof(CHARFORMAT2A);
1584 cr.cpMin = 21;
1585 cr.cpMax = 30;
1586 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1587 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1588
1589 /*Test that they are the same as plain text allows only one formatting*/
1590
1591 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1592 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1593 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1594
1595 /*Fill the control with a "wine" string, which when inserted will be bold*/
1596
1597 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1598
1599 /*Copy the bolded "wine" string*/
1600
1601 cr.cpMin = 0;
1602 cr.cpMax = 4;
1603 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1604 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1605
1606 /*Swap back to rich text*/
1607
1608 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1609 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1610
1611 /*Set the default formatting to bold italics*/
1612
1613 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1614 cf2.dwMask |= CFM_ITALIC;
1615 cf2.dwEffects ^= CFE_ITALIC;
1616 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1617 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1618
1619 /*Set the text in the control to "wine", which will be bold and italicized*/
1620
1621 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1622
1623 /*Paste the plain text "wine" string, which should take the insert
1624 formatting, which at the moment is bold italics*/
1625
1626 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1627
1628 /*Select the first "wine" string and retrieve its formatting*/
1629
1630 cr.cpMin = 1;
1631 cr.cpMax = 3;
1632 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1633 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1634
1635 /*Select the second "wine" string and retrieve its formatting*/
1636
1637 cr.cpMin = 5;
1638 cr.cpMax = 7;
1639 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1640 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1641
1642 /*Compare the two formattings. They should be the same.*/
1643
1644 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1645 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1646 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1647 DestroyWindow(hwndRichEdit);
1648 }
1649
test_WM_GETTEXT(void)1650 static void test_WM_GETTEXT(void)
1651 {
1652 HWND hwndRichEdit = new_richedit(NULL);
1653 static const char text[] = "Hello. My name is RichEdit!";
1654 static const char text2[] = "Hello. My name is RichEdit!\r";
1655 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1656 char buffer[1024] = {0};
1657 int result;
1658
1659 /* Baseline test with normal-sized buffer */
1660 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1661 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1662 ok(result == lstrlenA(buffer),
1663 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1664 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1665 result = strcmp(buffer,text);
1666 ok(result == 0,
1667 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1668
1669 /* Test for returned value of WM_GETTEXTLENGTH */
1670 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1671 ok(result == lstrlenA(text),
1672 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1673 result, lstrlenA(text));
1674
1675 /* Test for behavior in overflow case */
1676 memset(buffer, 0, 1024);
1677 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1678 ok(result == 0 ||
1679 result == lstrlenA(text) - 1, /* XP, win2k3 */
1680 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1681 result = strcmp(buffer,text);
1682 if (result)
1683 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1684 ok(result == 0,
1685 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1686
1687 /* Baseline test with normal-sized buffer and carriage return */
1688 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1689 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1690 ok(result == lstrlenA(buffer),
1691 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1692 result = strcmp(buffer,text2_after);
1693 ok(result == 0,
1694 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1695
1696 /* Test for returned value of WM_GETTEXTLENGTH */
1697 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1698 ok(result == lstrlenA(text2_after),
1699 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1700 result, lstrlenA(text2_after));
1701
1702 /* Test for behavior of CRLF conversion in case of overflow */
1703 memset(buffer, 0, 1024);
1704 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1705 ok(result == 0 ||
1706 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1707 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1708 result = strcmp(buffer,text2);
1709 if (result)
1710 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1711 ok(result == 0,
1712 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1713
1714 DestroyWindow(hwndRichEdit);
1715 }
1716
test_EM_GETTEXTRANGE(void)1717 static void test_EM_GETTEXTRANGE(void)
1718 {
1719 HWND hwndRichEdit = new_richedit(NULL);
1720 const char * text1 = "foo bar\r\nfoo bar";
1721 const char * text2 = "foo bar\rfoo bar";
1722 const char * expect = "bar\rfoo";
1723 char buffer[1024] = {0};
1724 LRESULT result;
1725 TEXTRANGEA textRange;
1726
1727 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1728
1729 textRange.lpstrText = buffer;
1730 textRange.chrg.cpMin = 4;
1731 textRange.chrg.cpMax = 11;
1732 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1733 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1734 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1735
1736 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1737
1738 textRange.lpstrText = buffer;
1739 textRange.chrg.cpMin = 4;
1740 textRange.chrg.cpMax = 11;
1741 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1742 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1743 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1744
1745 /* cpMax of text length is used instead of -1 in this case */
1746 textRange.lpstrText = buffer;
1747 textRange.chrg.cpMin = 0;
1748 textRange.chrg.cpMax = -1;
1749 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1750 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1751 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1752
1753 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1754 textRange.lpstrText = buffer;
1755 textRange.chrg.cpMin = -1;
1756 textRange.chrg.cpMax = 1;
1757 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1758 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1759 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1760
1761 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1762 textRange.lpstrText = buffer;
1763 textRange.chrg.cpMin = 1;
1764 textRange.chrg.cpMax = -1;
1765 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1766 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1767 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1768
1769 /* no end character is copied if cpMax - cpMin < 0 */
1770 textRange.lpstrText = buffer;
1771 textRange.chrg.cpMin = 5;
1772 textRange.chrg.cpMax = 5;
1773 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1774 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1775 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1776
1777 /* cpMax of text length is used if cpMax > text length*/
1778 textRange.lpstrText = buffer;
1779 textRange.chrg.cpMin = 0;
1780 textRange.chrg.cpMax = 1000;
1781 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1782 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1783 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1784
1785 /* Test with multibyte character */
1786 if (!is_lang_japanese)
1787 skip("Skip multibyte character tests on non-Japanese platform\n");
1788 else
1789 {
1790 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1791 textRange.chrg.cpMin = 4;
1792 textRange.chrg.cpMax = 8;
1793 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1794 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1795 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1796 }
1797
1798 DestroyWindow(hwndRichEdit);
1799 }
1800
test_EM_GETSELTEXT(void)1801 static void test_EM_GETSELTEXT(void)
1802 {
1803 HWND hwndRichEdit = new_richedit(NULL);
1804 const char * text1 = "foo bar\r\nfoo bar";
1805 const char * text2 = "foo bar\rfoo bar";
1806 const char * expect = "bar\rfoo";
1807 char buffer[1024] = {0};
1808 LRESULT result;
1809
1810 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1811
1812 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1813 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1814 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1815 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1816
1817 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1818
1819 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1820 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1821 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1822 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1823
1824 /* Test with multibyte character */
1825 if (!is_lang_japanese)
1826 skip("Skip multibyte character tests on non-Japanese platform\n");
1827 else
1828 {
1829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1830 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1831 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1832 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1833 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1834 }
1835
1836 DestroyWindow(hwndRichEdit);
1837 }
1838
1839 /* FIXME: need to test unimplemented options and robustly test wparam */
test_EM_SETOPTIONS(void)1840 static void test_EM_SETOPTIONS(void)
1841 {
1842 HWND hwndRichEdit;
1843 static const char text[] = "Hello. My name is RichEdit!";
1844 char buffer[1024] = {0};
1845 DWORD dwStyle, options, oldOptions;
1846 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1847 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1848 ES_SELECTIONBAR|ES_VERTICAL;
1849
1850 /* Test initial options. */
1851 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1852 0, 0, 200, 60, NULL, NULL,
1853 hmoduleRichEdit, NULL);
1854 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1855 RICHEDIT_CLASS20A, (int) GetLastError());
1856 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1857 ok(options == 0, "Incorrect initial options %x\n", options);
1858 DestroyWindow(hwndRichEdit);
1859
1860 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1861 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1862 0, 0, 200, 60, NULL, NULL,
1863 hmoduleRichEdit, NULL);
1864 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1865 RICHEDIT_CLASS20A, (int) GetLastError());
1866 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1867 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1868 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1869 "Incorrect initial options %x\n", options);
1870
1871 /* NEGATIVE TESTING - NO OPTIONS SET */
1872 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1873 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1874
1875 /* testing no readonly by sending 'a' to the control*/
1876 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1877 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1878 ok(buffer[0]=='a',
1879 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1880 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1881
1882 /* READONLY - sending 'a' to the control */
1883 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1884 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1885 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1886 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1887 ok(buffer[0]==text[0],
1888 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1889
1890 /* EM_SETOPTIONS changes the window style, but changing the
1891 * window style does not change the options. */
1892 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1893 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1894 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1895 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1896 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1897 /* Confirm that the text is still read only. */
1898 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1899 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1900 ok(buffer[0]==text[0],
1901 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1902
1903 oldOptions = options;
1904 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1905 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1906 ok(options == oldOptions,
1907 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1908
1909 DestroyWindow(hwndRichEdit);
1910 }
1911
check_CFE_LINK_selection(HWND hwnd,int sel_start,int sel_end)1912 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1913 {
1914 CHARFORMAT2A text_format;
1915 text_format.cbSize = sizeof(text_format);
1916 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1917 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1918 return (text_format.dwEffects & CFE_LINK) != 0;
1919 }
1920
check_CFE_LINK_rcvd(HWND hwnd,BOOL is_url,const char * url)1921 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1922 {
1923 BOOL link_present = FALSE;
1924
1925 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1926 if (is_url)
1927 { /* control text is url; should get CFE_LINK */
1928 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1929 }
1930 else
1931 {
1932 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1933 }
1934 }
1935
new_static_wnd(HWND parent)1936 static HWND new_static_wnd(HWND parent) {
1937 return new_window("Static", 0, parent);
1938 }
1939
test_EM_AUTOURLDETECT(void)1940 static void test_EM_AUTOURLDETECT(void)
1941 {
1942 /* DO NOT change the properties of the first two elements. To shorten the
1943 tests, all tests after WM_SETTEXT test just the first two elements -
1944 one non-URL and one URL */
1945 struct urls_s {
1946 const char *text;
1947 BOOL is_url;
1948 } urls[12] = {
1949 {"winehq.org", FALSE},
1950 {"http://www.winehq.org", TRUE},
1951 {"http//winehq.org", FALSE},
1952 {"ww.winehq.org", FALSE},
1953 {"www.winehq.org", TRUE},
1954 {"ftp://192.168.1.1", TRUE},
1955 {"ftp//192.168.1.1", FALSE},
1956 {"mailto:your@email.com", TRUE},
1957 {"prospero:prosperoserver", TRUE},
1958 {"telnet:test", TRUE},
1959 {"news:newserver", TRUE},
1960 {"wais:waisserver", TRUE}
1961 };
1962
1963 int i, j;
1964 int urlRet=-1;
1965 HWND hwndRichEdit, parent;
1966
1967 /* All of the following should cause the URL to be detected */
1968 const char * templates_delim[] = {
1969 "This is some text with X on it",
1970 "This is some text with (X) on it",
1971 "This is some text with X\r on it",
1972 "This is some text with ---X--- on it",
1973 "This is some text with \"X\" on it",
1974 "This is some text with 'X' on it",
1975 "This is some text with 'X' on it",
1976 "This is some text with :X: on it",
1977
1978 "This text ends with X",
1979
1980 "This is some text with X) on it",
1981 "This is some text with X--- on it",
1982 "This is some text with X\" on it",
1983 "This is some text with X' on it",
1984 "This is some text with X: on it",
1985
1986 "This is some text with (X on it",
1987 "This is some text with \rX on it",
1988 "This is some text with ---X on it",
1989 "This is some text with \"X on it",
1990 "This is some text with 'X on it",
1991 "This is some text with :X on it",
1992 };
1993 /* None of these should cause the URL to be detected */
1994 const char * templates_non_delim[] = {
1995 "This is some text with |X| on it",
1996 "This is some text with *X* on it",
1997 "This is some text with /X/ on it",
1998 "This is some text with +X+ on it",
1999 "This is some text with %X% on it",
2000 "This is some text with #X# on it",
2001 "This is some text with @X@ on it",
2002 "This is some text with \\X\\ on it",
2003 "This is some text with |X on it",
2004 "This is some text with *X on it",
2005 "This is some text with /X on it",
2006 "This is some text with +X on it",
2007 "This is some text with %X on it",
2008 "This is some text with #X on it",
2009 "This is some text with @X on it",
2010 "This is some text with \\X on it",
2011 "This is some text with _X on it",
2012 };
2013 /* All of these cause the URL detection to be extended by one more byte,
2014 thus demonstrating that the tested character is considered as part
2015 of the URL. */
2016 const char * templates_xten_delim[] = {
2017 "This is some text with X| on it",
2018 "This is some text with X* on it",
2019 "This is some text with X/ on it",
2020 "This is some text with X+ on it",
2021 "This is some text with X% on it",
2022 "This is some text with X# on it",
2023 "This is some text with X@ on it",
2024 "This is some text with X\\ on it",
2025 "This is some text with X_ on it",
2026 };
2027 /* These delims act as neutral breaks. Whether the url is ended
2028 or not depends on the next non-neutral character. We'll test
2029 with Y unchanged, in which case the url should include the
2030 deliminator and the Y. We'll also test with the Y changed
2031 to a space, in which case the url stops before the
2032 deliminator. */
2033 const char * templates_neutral_delim[] = {
2034 "This is some text with X-Y on it",
2035 "This is some text with X--Y on it",
2036 "This is some text with X!Y on it",
2037 "This is some text with X[Y on it",
2038 "This is some text with X]Y on it",
2039 "This is some text with X{Y on it",
2040 "This is some text with X}Y on it",
2041 "This is some text with X(Y on it",
2042 "This is some text with X)Y on it",
2043 "This is some text with X\"Y on it",
2044 "This is some text with X;Y on it",
2045 "This is some text with X:Y on it",
2046 "This is some text with X'Y on it",
2047 "This is some text with X?Y on it",
2048 "This is some text with X<Y on it",
2049 "This is some text with X>Y on it",
2050 "This is some text with X.Y on it",
2051 "This is some text with X,Y on it",
2052 };
2053 char buffer[1024];
2054
2055 parent = new_static_wnd(NULL);
2056 hwndRichEdit = new_richedit(parent);
2057 /* Try and pass EM_AUTOURLDETECT some test wParam values */
2058 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2059 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2060 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2061 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2062 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2063 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2064 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2065 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2066 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2067 /* for each url, check the text to see if CFE_LINK effect is present */
2068 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2069
2070 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2071 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2072 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2073
2074 /* Link detection should happen immediately upon WM_SETTEXT */
2075 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2077 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2078 }
2079 DestroyWindow(hwndRichEdit);
2080
2081 /* Test detection of URLs within normal text - WM_SETTEXT case. */
2082 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2083 hwndRichEdit = new_richedit(parent);
2084
2085 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2086 char * at_pos;
2087 int at_offset;
2088 int end_offset;
2089
2090 at_pos = strchr(templates_delim[j], 'X');
2091 at_offset = at_pos - templates_delim[j];
2092 memcpy(buffer, templates_delim[j], at_offset);
2093 buffer[at_offset] = '\0';
2094 strcat(buffer, urls[i].text);
2095 strcat(buffer, templates_delim[j] + at_offset + 1);
2096 end_offset = at_offset + strlen(urls[i].text);
2097
2098 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2099 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2100
2101 /* This assumes no templates start with the URL itself, and that they
2102 have at least two characters before the URL text */
2103 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2104 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2105 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2106 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2107 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2108 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2109
2110 if (urls[i].is_url)
2111 {
2112 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2114 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2116 }
2117 else
2118 {
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2121 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2122 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2123 }
2124 if (buffer[end_offset] != '\0')
2125 {
2126 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2127 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2128 if (buffer[end_offset +1] != '\0')
2129 {
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2132 }
2133 }
2134 }
2135
2136 for (j = 0; j < ARRAY_SIZE(templates_non_delim); j++) {
2137 char * at_pos;
2138 int at_offset;
2139 int end_offset;
2140
2141 at_pos = strchr(templates_non_delim[j], 'X');
2142 at_offset = at_pos - templates_non_delim[j];
2143 memcpy(buffer, templates_non_delim[j], at_offset);
2144 buffer[at_offset] = '\0';
2145 strcat(buffer, urls[i].text);
2146 strcat(buffer, templates_non_delim[j] + at_offset + 1);
2147 end_offset = at_offset + strlen(urls[i].text);
2148
2149 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2150 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2151
2152 /* This assumes no templates start with the URL itself, and that they
2153 have at least two characters before the URL text */
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2158 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2159 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2160
2161 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2163 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
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 for (j = 0; j < ARRAY_SIZE(templates_xten_delim); j++) {
2178 char * at_pos;
2179 int at_offset;
2180 int end_offset;
2181
2182 at_pos = strchr(templates_xten_delim[j], 'X');
2183 at_offset = at_pos - templates_xten_delim[j];
2184 memcpy(buffer, templates_xten_delim[j], at_offset);
2185 buffer[at_offset] = '\0';
2186 strcat(buffer, urls[i].text);
2187 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2188 end_offset = at_offset + strlen(urls[i].text);
2189
2190 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2191 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2192
2193 /* This assumes no templates start with the URL itself, and that they
2194 have at least two characters before the URL text */
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2197 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2198 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2201
2202 if (urls[i].is_url)
2203 {
2204 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2206 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2208 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2209 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2210 }
2211 else
2212 {
2213 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2214 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2215 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2216 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2217 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2218 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2219 }
2220 if (buffer[end_offset +1] != '\0')
2221 {
2222 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2223 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2224 if (buffer[end_offset +2] != '\0')
2225 {
2226 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2227 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2228 }
2229 }
2230 }
2231
2232 for (j = 0; j < ARRAY_SIZE(templates_neutral_delim); j++) {
2233 char * at_pos, * end_pos;
2234 int at_offset;
2235 int end_offset;
2236
2237 if (!urls[i].is_url) continue;
2238
2239 at_pos = strchr(templates_neutral_delim[j], 'X');
2240 at_offset = at_pos - templates_neutral_delim[j];
2241 memcpy(buffer, templates_neutral_delim[j], at_offset);
2242 buffer[at_offset] = '\0';
2243 strcat(buffer, urls[i].text);
2244 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2245
2246 end_pos = strchr(buffer, 'Y');
2247 end_offset = end_pos - buffer;
2248
2249 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2250 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2251
2252 /* This assumes no templates start with the URL itself, and that they
2253 have at least two characters before the URL text */
2254 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2255 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2256 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2257 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2258 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2259 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2260
2261 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2262 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2263 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2264 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2265 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2266 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2267
2268 *end_pos = ' ';
2269
2270 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2271 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2272
2273 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2274 "CFE_LINK not 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 set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2277 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2278 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2279 }
2280
2281 DestroyWindow(hwndRichEdit);
2282 hwndRichEdit = NULL;
2283 }
2284
2285 /* Test detection of URLs within normal text - WM_CHAR case. */
2286 /* Test only the first two URL examples for brevity */
2287 for (i = 0; i < 2; i++) {
2288 hwndRichEdit = new_richedit(parent);
2289
2290 /* Also for brevity, test only the first three delimiters */
2291 for (j = 0; j < 3; j++) {
2292 char * at_pos;
2293 int at_offset;
2294 int end_offset;
2295 int u, v;
2296
2297 at_pos = strchr(templates_delim[j], 'X');
2298 at_offset = at_pos - templates_delim[j];
2299 end_offset = at_offset + strlen(urls[i].text);
2300
2301 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2302 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2303 for (u = 0; templates_delim[j][u]; u++) {
2304 if (templates_delim[j][u] == '\r') {
2305 simulate_typing_characters(hwndRichEdit, "\r");
2306 } else if (templates_delim[j][u] != 'X') {
2307 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2308 } else {
2309 for (v = 0; urls[i].text[v]; v++) {
2310 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2311 }
2312 }
2313 }
2314 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2315
2316 /* This assumes no templates start with the URL itself, and that they
2317 have at least two characters before the URL text */
2318 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2319 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2320 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2321 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2322 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2323 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2324
2325 if (urls[i].is_url)
2326 {
2327 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2328 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2329 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2330 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2331 }
2332 else
2333 {
2334 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2336 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2338 }
2339 if (buffer[end_offset] != '\0')
2340 {
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2343 if (buffer[end_offset +1] != '\0')
2344 {
2345 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2346 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2347 }
2348 }
2349
2350 /* The following will insert a paragraph break after the first character
2351 of the URL candidate, thus breaking the URL. It is expected that the
2352 CFE_LINK attribute should break across both pieces of the URL */
2353 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2354 simulate_typing_characters(hwndRichEdit, "\r");
2355 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2356
2357 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2358 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2359 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2360 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2361 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2362 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2363
2364 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2365 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2366 /* end_offset moved because of paragraph break */
2367 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2368 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2369 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2370 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2371 {
2372 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2373 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2374 if (buffer[end_offset +2] != '\0')
2375 {
2376 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2377 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2378 }
2379 }
2380
2381 /* The following will remove the just-inserted paragraph break, thus
2382 restoring the URL */
2383 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2384 simulate_typing_characters(hwndRichEdit, "\b");
2385 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2386
2387 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2388 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2389 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2390 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2391 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2392 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2393
2394 if (urls[i].is_url)
2395 {
2396 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2397 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2398 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2399 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2400 }
2401 else
2402 {
2403 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2404 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2405 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2406 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2407 }
2408 if (buffer[end_offset] != '\0')
2409 {
2410 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2411 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2412 if (buffer[end_offset +1] != '\0')
2413 {
2414 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2415 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2416 }
2417 }
2418 }
2419 DestroyWindow(hwndRichEdit);
2420 hwndRichEdit = NULL;
2421 }
2422
2423 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2424 /* Test just the first two URL examples for brevity */
2425 for (i = 0; i < 2; i++) {
2426 SETTEXTEX st;
2427
2428 hwndRichEdit = new_richedit(parent);
2429
2430 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2431 be detected:
2432 1) Set entire text, a la WM_SETTEXT
2433 2) Set a selection of the text to the URL
2434 3) Set a portion of the text at a time, which eventually results in
2435 an URL
2436 All of them should give equivalent results
2437 */
2438
2439 /* Set entire text in one go, like WM_SETTEXT */
2440 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2441 char * at_pos;
2442 int at_offset;
2443 int end_offset;
2444
2445 st.codepage = CP_ACP;
2446 st.flags = ST_DEFAULT;
2447
2448 at_pos = strchr(templates_delim[j], 'X');
2449 at_offset = at_pos - templates_delim[j];
2450 memcpy(buffer, templates_delim[j], at_offset);
2451 buffer[at_offset] = '\0';
2452 strcat(buffer, urls[i].text);
2453 strcat(buffer, templates_delim[j] + at_offset + 1);
2454 end_offset = at_offset + strlen(urls[i].text);
2455
2456 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2457 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2458
2459 /* This assumes no templates start with the URL itself, and that they
2460 have at least two characters before the URL text */
2461 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2462 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2463 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2464 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2465 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2466 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2467
2468 if (urls[i].is_url)
2469 {
2470 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2471 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2472 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2473 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2474 }
2475 else
2476 {
2477 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2478 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2479 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2480 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2481 }
2482 if (buffer[end_offset] != '\0')
2483 {
2484 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2485 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2486 if (buffer[end_offset +1] != '\0')
2487 {
2488 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2489 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2490 }
2491 }
2492 }
2493
2494 /* Set selection with X to the URL */
2495 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2496 char * at_pos;
2497 int at_offset;
2498 int end_offset;
2499
2500 at_pos = strchr(templates_delim[j], 'X');
2501 at_offset = at_pos - templates_delim[j];
2502 end_offset = at_offset + strlen(urls[i].text);
2503
2504 st.codepage = CP_ACP;
2505 st.flags = ST_DEFAULT;
2506 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2507 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2508 st.flags = ST_SELECTION;
2509 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2510 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2511 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2512
2513 /* This assumes no templates start with the URL itself, and that they
2514 have at least two characters before the URL text */
2515 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2516 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2517 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2518 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2519 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2520 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2521
2522 if (urls[i].is_url)
2523 {
2524 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2525 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2526 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2527 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2528 }
2529 else
2530 {
2531 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2532 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2533 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2534 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2535 }
2536 if (buffer[end_offset] != '\0')
2537 {
2538 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2539 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2540 if (buffer[end_offset +1] != '\0')
2541 {
2542 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2543 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2544 }
2545 }
2546 }
2547
2548 /* Set selection with X to the first character of the URL, then the rest */
2549 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2550 char * at_pos;
2551 int at_offset;
2552 int end_offset;
2553
2554 at_pos = strchr(templates_delim[j], 'X');
2555 at_offset = at_pos - templates_delim[j];
2556 end_offset = at_offset + strlen(urls[i].text);
2557
2558 strcpy(buffer, "YY");
2559 buffer[0] = urls[i].text[0];
2560
2561 st.codepage = CP_ACP;
2562 st.flags = ST_DEFAULT;
2563 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2564 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2565 st.flags = ST_SELECTION;
2566 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2567 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2568 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2569 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2570 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2571
2572 /* This assumes no templates start with the URL itself, and that they
2573 have at least two characters before the URL text */
2574 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2576 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2578 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2579 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2580
2581 if (urls[i].is_url)
2582 {
2583 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2584 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2585 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2586 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2587 }
2588 else
2589 {
2590 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2591 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2592 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2593 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2594 }
2595 if (buffer[end_offset] != '\0')
2596 {
2597 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2598 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2599 if (buffer[end_offset +1] != '\0')
2600 {
2601 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2602 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2603 }
2604 }
2605 }
2606
2607 DestroyWindow(hwndRichEdit);
2608 hwndRichEdit = NULL;
2609 }
2610
2611 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2612 /* Test just the first two URL examples for brevity */
2613 for (i = 0; i < 2; i++) {
2614 hwndRichEdit = new_richedit(parent);
2615
2616 /* Set selection with X to the URL */
2617 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2618 char * at_pos;
2619 int at_offset;
2620 int end_offset;
2621
2622 at_pos = strchr(templates_delim[j], 'X');
2623 at_offset = at_pos - templates_delim[j];
2624 end_offset = at_offset + strlen(urls[i].text);
2625
2626 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2627 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2628 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2629 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2630 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2631
2632 /* This assumes no templates start with the URL itself, and that they
2633 have at least two characters before the URL text */
2634 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2635 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2636 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2637 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2638 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2640
2641 if (urls[i].is_url)
2642 {
2643 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2644 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2645 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2646 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2647 }
2648 else
2649 {
2650 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2651 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2652 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2653 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2654 }
2655 if (buffer[end_offset] != '\0')
2656 {
2657 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2658 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2659 if (buffer[end_offset +1] != '\0')
2660 {
2661 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2662 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2663 }
2664 }
2665 }
2666
2667 /* Set selection with X to the first character of the URL, then the rest */
2668 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2669 char * at_pos;
2670 int at_offset;
2671 int end_offset;
2672
2673 at_pos = strchr(templates_delim[j], 'X');
2674 at_offset = at_pos - templates_delim[j];
2675 end_offset = at_offset + strlen(urls[i].text);
2676
2677 strcpy(buffer, "YY");
2678 buffer[0] = urls[i].text[0];
2679
2680 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2681 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2682 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2683 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2684 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2685 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2686 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2687
2688 /* This assumes no templates start with the URL itself, and that they
2689 have at least two characters before the URL text */
2690 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2692 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2693 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2694 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2696
2697 if (urls[i].is_url)
2698 {
2699 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2700 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2701 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2702 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2703 }
2704 else
2705 {
2706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2710 }
2711 if (buffer[end_offset] != '\0')
2712 {
2713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2715 if (buffer[end_offset +1] != '\0')
2716 {
2717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2719 }
2720 }
2721 }
2722
2723 DestroyWindow(hwndRichEdit);
2724 hwndRichEdit = NULL;
2725 }
2726
2727 DestroyWindow(parent);
2728 }
2729
test_EM_SCROLL(void)2730 static void test_EM_SCROLL(void)
2731 {
2732 int i, j;
2733 int r; /* return value */
2734 int expr; /* expected return value */
2735 HWND hwndRichEdit = new_richedit(NULL);
2736 int y_before, y_after; /* units of lines of text */
2737
2738 /* test a richedit box containing a single line of text */
2739 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2740 expr = 0x00010000;
2741 for (i = 0; i < 4; i++) {
2742 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2743
2744 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2745 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2746 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2747 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2748 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2749 "(i == %d)\n", y_after, i);
2750 }
2751
2752 /*
2753 * test a richedit box that will scroll. There are two general
2754 * cases: the case without any long lines and the case with a long
2755 * line.
2756 */
2757 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2758 if (i == 0)
2759 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2760 else
2761 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2762 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2763 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2764 "LONG LINE \nb\nc\nd\ne");
2765 for (j = 0; j < 12; j++) /* reset scroll position to top */
2766 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2767
2768 /* get first visible line */
2769 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2770 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2771
2772 /* get new current first visible line */
2773 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2774
2775 ok(((r & 0xffffff00) == 0x00010000) &&
2776 ((r & 0x000000ff) != 0x00000000),
2777 "EM_SCROLL page down didn't scroll by a small positive number of "
2778 "lines (r == 0x%08x)\n", r);
2779 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2780 "(line %d scrolled to line %d\n", y_before, y_after);
2781
2782 y_before = y_after;
2783
2784 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2785 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2786 ok(((r & 0xffffff00) == 0x0001ff00),
2787 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2788 "(r == 0x%08x)\n", r);
2789 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2790 "%d scrolled to line %d\n", y_before, y_after);
2791
2792 y_before = y_after;
2793
2794 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2795
2796 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2797
2798 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2799 "(r == 0x%08x)\n", r);
2800 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2801 "1 line (%d scrolled to %d)\n", y_before, y_after);
2802
2803 y_before = y_after;
2804
2805 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2806
2807 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2808
2809 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2810 "(r == 0x%08x)\n", r);
2811 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2812 "line (%d scrolled to %d)\n", y_before, y_after);
2813
2814 y_before = y_after;
2815
2816 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2817 SB_LINEUP, 0); /* lineup beyond top */
2818
2819 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2820
2821 ok(r == 0x00010000,
2822 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2823 ok(y_before == y_after,
2824 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2825
2826 y_before = y_after;
2827
2828 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2829 SB_PAGEUP, 0);/*page up beyond top */
2830
2831 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2832
2833 ok(r == 0x00010000,
2834 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2835 ok(y_before == y_after,
2836 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2837
2838 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2839 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2840 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2841 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2842 SB_PAGEDOWN, 0); /* page down beyond bot */
2843 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2844
2845 ok(r == 0x00010000,
2846 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2847 ok(y_before == y_after,
2848 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2849 y_before, y_after);
2850
2851 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2852 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2853 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2854
2855 ok(r == 0x00010000,
2856 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2857 ok(y_before == y_after,
2858 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2859 y_before, y_after);
2860 }
2861 DestroyWindow(hwndRichEdit);
2862 }
2863
2864 static unsigned int recursionLevel = 0;
2865 static unsigned int WM_SIZE_recursionLevel = 0;
2866 static BOOL bailedOutOfRecursion = FALSE;
2867 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2868
RicheditStupidOverrideProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)2869 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2870 {
2871 LRESULT r;
2872
2873 if (bailedOutOfRecursion) return 0;
2874 if (recursionLevel >= 32) {
2875 bailedOutOfRecursion = TRUE;
2876 return 0;
2877 }
2878
2879 recursionLevel++;
2880 switch (message) {
2881 case WM_SIZE:
2882 WM_SIZE_recursionLevel++;
2883 r = richeditProc(hwnd, message, wParam, lParam);
2884 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2885 ShowScrollBar(hwnd, SB_VERT, TRUE);
2886 WM_SIZE_recursionLevel--;
2887 break;
2888 default:
2889 r = richeditProc(hwnd, message, wParam, lParam);
2890 break;
2891 }
2892 recursionLevel--;
2893 return r;
2894 }
2895
test_scrollbar_visibility(void)2896 static void test_scrollbar_visibility(void)
2897 {
2898 HWND hwndRichEdit;
2899 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2900 SCROLLINFO si;
2901 WNDCLASSA cls;
2902 BOOL r;
2903
2904 /* These tests show that richedit should temporarily refrain from automatically
2905 hiding or showing its scrollbars (vertical at least) when an explicit request
2906 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2907 Some applications depend on forced showing (when otherwise richedit would
2908 hide the vertical scrollbar) and are thrown on an endless recursive loop
2909 if richedit auto-hides the scrollbar again. Apparently they never heard of
2910 the ES_DISABLENOSCROLL style... */
2911
2912 hwndRichEdit = new_richedit(NULL);
2913
2914 /* Test default scrollbar visibility behavior */
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2920 "Vertical scrollbar is visible, should be invisible.\n");
2921 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923 si.nPage, si.nMin, si.nMax);
2924
2925 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2926 memset(&si, 0, sizeof(si));
2927 si.cbSize = sizeof(si);
2928 si.fMask = SIF_PAGE | SIF_RANGE;
2929 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931 "Vertical scrollbar is visible, should be invisible.\n");
2932 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934 si.nPage, si.nMin, si.nMax);
2935
2936 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2937 memset(&si, 0, sizeof(si));
2938 si.cbSize = sizeof(si);
2939 si.fMask = SIF_PAGE | SIF_RANGE;
2940 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2942 "Vertical scrollbar is invisible, should be visible.\n");
2943 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2944 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2945 si.nPage, si.nMin, si.nMax);
2946
2947 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2948 even though it hides the scrollbar */
2949 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950 memset(&si, 0, sizeof(si));
2951 si.cbSize = sizeof(si);
2952 si.fMask = SIF_PAGE | SIF_RANGE;
2953 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955 "Vertical scrollbar is visible, should be invisible.\n");
2956 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958 si.nPage, si.nMin, si.nMax);
2959
2960 /* Setting non-scrolling text again does *not* reset scrollbar range */
2961 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2962 memset(&si, 0, sizeof(si));
2963 si.cbSize = sizeof(si);
2964 si.fMask = SIF_PAGE | SIF_RANGE;
2965 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2966 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2967 "Vertical scrollbar is visible, should be invisible.\n");
2968 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2969 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2970 si.nPage, si.nMin, si.nMax);
2971
2972 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2973 memset(&si, 0, sizeof(si));
2974 si.cbSize = sizeof(si);
2975 si.fMask = SIF_PAGE | SIF_RANGE;
2976 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2977 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2978 "Vertical scrollbar is visible, should be invisible.\n");
2979 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2980 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2981 si.nPage, si.nMin, si.nMax);
2982
2983 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2984 memset(&si, 0, sizeof(si));
2985 si.cbSize = sizeof(si);
2986 si.fMask = SIF_PAGE | SIF_RANGE;
2987 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2988 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2989 "Vertical scrollbar is visible, should be invisible.\n");
2990 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2991 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2992 si.nPage, si.nMin, si.nMax);
2993
2994 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2995 memset(&si, 0, sizeof(si));
2996 si.cbSize = sizeof(si);
2997 si.fMask = SIF_PAGE | SIF_RANGE;
2998 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2999 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3000 "Vertical scrollbar is visible, should be invisible.\n");
3001 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3002 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3003 si.nPage, si.nMin, si.nMax);
3004
3005 DestroyWindow(hwndRichEdit);
3006
3007 /* Test again, with ES_DISABLENOSCROLL style */
3008 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
3009
3010 /* Test default scrollbar visibility behavior */
3011 memset(&si, 0, sizeof(si));
3012 si.cbSize = sizeof(si);
3013 si.fMask = SIF_PAGE | SIF_RANGE;
3014 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016 "Vertical scrollbar is invisible, should be visible.\n");
3017 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3018 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3019 si.nPage, si.nMin, si.nMax);
3020
3021 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3022 memset(&si, 0, sizeof(si));
3023 si.cbSize = sizeof(si);
3024 si.fMask = SIF_PAGE | SIF_RANGE;
3025 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3027 "Vertical scrollbar is invisible, should be visible.\n");
3028 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3029 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3030 si.nPage, si.nMin, si.nMax);
3031
3032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3033 memset(&si, 0, sizeof(si));
3034 si.cbSize = sizeof(si);
3035 si.fMask = SIF_PAGE | SIF_RANGE;
3036 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3038 "Vertical scrollbar is invisible, should be visible.\n");
3039 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3040 "reported page/range is %d (%d..%d)\n",
3041 si.nPage, si.nMin, si.nMax);
3042
3043 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3044 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3045 memset(&si, 0, sizeof(si));
3046 si.cbSize = sizeof(si);
3047 si.fMask = SIF_PAGE | SIF_RANGE;
3048 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050 "Vertical scrollbar is invisible, should be visible.\n");
3051 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3052 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3053 si.nPage, si.nMin, si.nMax);
3054
3055 /* Setting non-scrolling text again does *not* reset scrollbar range */
3056 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3057 memset(&si, 0, sizeof(si));
3058 si.cbSize = sizeof(si);
3059 si.fMask = SIF_PAGE | SIF_RANGE;
3060 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3061 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3062 "Vertical scrollbar is invisible, should be visible.\n");
3063 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3064 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3065 si.nPage, si.nMin, si.nMax);
3066
3067 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3068 memset(&si, 0, sizeof(si));
3069 si.cbSize = sizeof(si);
3070 si.fMask = SIF_PAGE | SIF_RANGE;
3071 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3072 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3073 "Vertical scrollbar is invisible, should be visible.\n");
3074 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3075 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3076 si.nPage, si.nMin, si.nMax);
3077
3078 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084 "Vertical scrollbar is invisible, should be visible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3086 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3087 si.nPage, si.nMin, si.nMax);
3088
3089 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3090 memset(&si, 0, sizeof(si));
3091 si.cbSize = sizeof(si);
3092 si.fMask = SIF_PAGE | SIF_RANGE;
3093 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3094 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3095 "Vertical scrollbar is invisible, should be visible.\n");
3096 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3097 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3098 si.nPage, si.nMin, si.nMax);
3099
3100 DestroyWindow(hwndRichEdit);
3101
3102 /* Test behavior with explicit visibility request, using ShowScrollBar() */
3103 hwndRichEdit = new_richedit(NULL);
3104
3105 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3106 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3107 memset(&si, 0, sizeof(si));
3108 si.cbSize = sizeof(si);
3109 si.fMask = SIF_PAGE | SIF_RANGE;
3110 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3112 "Vertical scrollbar is invisible, should be visible.\n");
3113 todo_wine {
3114 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3115 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3116 si.nPage, si.nMin, si.nMax);
3117 }
3118
3119 /* Ditto, see above */
3120 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3121 memset(&si, 0, sizeof(si));
3122 si.cbSize = sizeof(si);
3123 si.fMask = SIF_PAGE | SIF_RANGE;
3124 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126 "Vertical scrollbar is invisible, should be visible.\n");
3127 todo_wine {
3128 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3129 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3130 si.nPage, si.nMin, si.nMax);
3131 }
3132
3133 /* Ditto, see above */
3134 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3135 memset(&si, 0, sizeof(si));
3136 si.cbSize = sizeof(si);
3137 si.fMask = SIF_PAGE | SIF_RANGE;
3138 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3139 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3140 "Vertical scrollbar is invisible, should be visible.\n");
3141 todo_wine {
3142 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3143 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3144 si.nPage, si.nMin, si.nMax);
3145 }
3146
3147 /* Ditto, see above */
3148 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3149 memset(&si, 0, sizeof(si));
3150 si.cbSize = sizeof(si);
3151 si.fMask = SIF_PAGE | SIF_RANGE;
3152 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3153 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3154 "Vertical scrollbar is invisible, should be visible.\n");
3155 todo_wine {
3156 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3157 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3158 si.nPage, si.nMin, si.nMax);
3159 }
3160
3161 /* Ditto, see above */
3162 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3163 memset(&si, 0, sizeof(si));
3164 si.cbSize = sizeof(si);
3165 si.fMask = SIF_PAGE | SIF_RANGE;
3166 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3168 "Vertical scrollbar is invisible, should be visible.\n");
3169 todo_wine {
3170 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3171 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3172 si.nPage, si.nMin, si.nMax);
3173 }
3174
3175 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3176 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3177 memset(&si, 0, sizeof(si));
3178 si.cbSize = sizeof(si);
3179 si.fMask = SIF_PAGE | SIF_RANGE;
3180 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3181 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3182 "Vertical scrollbar is visible, should be invisible.\n");
3183 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3184 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3185 si.nPage, si.nMin, si.nMax);
3186
3187 DestroyWindow(hwndRichEdit);
3188
3189 hwndRichEdit = new_richedit(NULL);
3190
3191 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3192 memset(&si, 0, sizeof(si));
3193 si.cbSize = sizeof(si);
3194 si.fMask = SIF_PAGE | SIF_RANGE;
3195 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3196 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3197 "Vertical scrollbar is visible, should be invisible.\n");
3198 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3199 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3200 si.nPage, si.nMin, si.nMax);
3201
3202 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3203 memset(&si, 0, sizeof(si));
3204 si.cbSize = sizeof(si);
3205 si.fMask = SIF_PAGE | SIF_RANGE;
3206 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3207 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3208 "Vertical scrollbar is visible, should be invisible.\n");
3209 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3210 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3211 si.nPage, si.nMin, si.nMax);
3212
3213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3214 memset(&si, 0, sizeof(si));
3215 si.cbSize = sizeof(si);
3216 si.fMask = SIF_PAGE | SIF_RANGE;
3217 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3218 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3219 "Vertical scrollbar is visible, should be invisible.\n");
3220 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3221 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3222 si.nPage, si.nMin, si.nMax);
3223
3224 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3225 memset(&si, 0, sizeof(si));
3226 si.cbSize = sizeof(si);
3227 si.fMask = SIF_PAGE | SIF_RANGE;
3228 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3229 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3230 "Vertical scrollbar is visible, should be invisible.\n");
3231 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3232 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3233 si.nPage, si.nMin, si.nMax);
3234
3235 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3236 memset(&si, 0, sizeof(si));
3237 si.cbSize = sizeof(si);
3238 si.fMask = SIF_PAGE | SIF_RANGE;
3239 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3240 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3241 "Vertical scrollbar is invisible, should be visible.\n");
3242 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3243 "reported page/range is %d (%d..%d)\n",
3244 si.nPage, si.nMin, si.nMax);
3245
3246 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3247 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3248 memset(&si, 0, sizeof(si));
3249 si.cbSize = sizeof(si);
3250 si.fMask = SIF_PAGE | SIF_RANGE;
3251 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3252 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3253 "Vertical scrollbar is visible, should be invisible.\n");
3254 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3255 "reported page/range is %d (%d..%d)\n",
3256 si.nPage, si.nMin, si.nMax);
3257
3258 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3259 memset(&si, 0, sizeof(si));
3260 si.cbSize = sizeof(si);
3261 si.fMask = SIF_PAGE | SIF_RANGE;
3262 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3263 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3264 "Vertical scrollbar is visible, should be invisible.\n");
3265 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3266 "reported page/range is %d (%d..%d)\n",
3267 si.nPage, si.nMin, si.nMax);
3268
3269 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3270 EM_SCROLL will make visible any forcefully invisible scrollbar */
3271 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3272 memset(&si, 0, sizeof(si));
3273 si.cbSize = sizeof(si);
3274 si.fMask = SIF_PAGE | SIF_RANGE;
3275 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3276 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3277 "Vertical scrollbar is invisible, should be visible.\n");
3278 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3279 "reported page/range is %d (%d..%d)\n",
3280 si.nPage, si.nMin, si.nMax);
3281
3282 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3283 memset(&si, 0, sizeof(si));
3284 si.cbSize = sizeof(si);
3285 si.fMask = SIF_PAGE | SIF_RANGE;
3286 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3287 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3288 "Vertical scrollbar is visible, should be invisible.\n");
3289 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3290 "reported page/range is %d (%d..%d)\n",
3291 si.nPage, si.nMin, si.nMax);
3292
3293 /* Again, EM_SCROLL, with SB_LINEUP */
3294 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3295 memset(&si, 0, sizeof(si));
3296 si.cbSize = sizeof(si);
3297 si.fMask = SIF_PAGE | SIF_RANGE;
3298 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3299 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3300 "Vertical scrollbar is invisible, should be visible.\n");
3301 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3302 "reported page/range is %d (%d..%d)\n",
3303 si.nPage, si.nMin, si.nMax);
3304
3305 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3306 memset(&si, 0, sizeof(si));
3307 si.cbSize = sizeof(si);
3308 si.fMask = SIF_PAGE | SIF_RANGE;
3309 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3310 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3311 "Vertical scrollbar is visible, should be invisible.\n");
3312 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3313 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3314 si.nPage, si.nMin, si.nMax);
3315
3316 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3317 memset(&si, 0, sizeof(si));
3318 si.cbSize = sizeof(si);
3319 si.fMask = SIF_PAGE | SIF_RANGE;
3320 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3321 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3322 "Vertical scrollbar is invisible, should be visible.\n");
3323 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3324 "reported page/range is %d (%d..%d)\n",
3325 si.nPage, si.nMin, si.nMax);
3326
3327 DestroyWindow(hwndRichEdit);
3328
3329
3330 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3331 hwndRichEdit = new_richedit(NULL);
3332
3333 #define ENABLE_WS_VSCROLL(hwnd) \
3334 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3335 #define DISABLE_WS_VSCROLL(hwnd) \
3336 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3337
3338 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3339 ENABLE_WS_VSCROLL(hwndRichEdit);
3340 memset(&si, 0, sizeof(si));
3341 si.cbSize = sizeof(si);
3342 si.fMask = SIF_PAGE | SIF_RANGE;
3343 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3344 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3345 "Vertical scrollbar is invisible, should be visible.\n");
3346 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3347 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3348 si.nPage, si.nMin, si.nMax);
3349
3350 /* Ditto, see above */
3351 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3352 memset(&si, 0, sizeof(si));
3353 si.cbSize = sizeof(si);
3354 si.fMask = SIF_PAGE | SIF_RANGE;
3355 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3356 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3357 "Vertical scrollbar is invisible, should be visible.\n");
3358 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3359 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3360 si.nPage, si.nMin, si.nMax);
3361
3362 /* Ditto, see above */
3363 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3364 memset(&si, 0, sizeof(si));
3365 si.cbSize = sizeof(si);
3366 si.fMask = SIF_PAGE | SIF_RANGE;
3367 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3368 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3369 "Vertical scrollbar is invisible, should be visible.\n");
3370 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3371 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3372 si.nPage, si.nMin, si.nMax);
3373
3374 /* Ditto, see above */
3375 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3376 memset(&si, 0, sizeof(si));
3377 si.cbSize = sizeof(si);
3378 si.fMask = SIF_PAGE | SIF_RANGE;
3379 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3380 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3381 "Vertical scrollbar is invisible, should be visible.\n");
3382 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3383 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3384 si.nPage, si.nMin, si.nMax);
3385
3386 /* Ditto, see above */
3387 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3388 memset(&si, 0, sizeof(si));
3389 si.cbSize = sizeof(si);
3390 si.fMask = SIF_PAGE | SIF_RANGE;
3391 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3392 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3393 "Vertical scrollbar is invisible, should be visible.\n");
3394 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3395 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3396 si.nPage, si.nMin, si.nMax);
3397
3398 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3399 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3400 memset(&si, 0, sizeof(si));
3401 si.cbSize = sizeof(si);
3402 si.fMask = SIF_PAGE | SIF_RANGE;
3403 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3404 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3405 "Vertical scrollbar is visible, should be invisible.\n");
3406 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3407 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3408 si.nPage, si.nMin, si.nMax);
3409
3410 DestroyWindow(hwndRichEdit);
3411
3412 hwndRichEdit = new_richedit(NULL);
3413
3414 DISABLE_WS_VSCROLL(hwndRichEdit);
3415 memset(&si, 0, sizeof(si));
3416 si.cbSize = sizeof(si);
3417 si.fMask = SIF_PAGE | SIF_RANGE;
3418 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3419 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3420 "Vertical scrollbar is visible, should be invisible.\n");
3421 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3422 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3423 si.nPage, si.nMin, si.nMax);
3424
3425 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3426 memset(&si, 0, sizeof(si));
3427 si.cbSize = sizeof(si);
3428 si.fMask = SIF_PAGE | SIF_RANGE;
3429 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3430 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3431 "Vertical scrollbar is visible, should be invisible.\n");
3432 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3433 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3434 si.nPage, si.nMin, si.nMax);
3435
3436 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3437 memset(&si, 0, sizeof(si));
3438 si.cbSize = sizeof(si);
3439 si.fMask = SIF_PAGE | SIF_RANGE;
3440 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3441 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3442 "Vertical scrollbar is visible, should be invisible.\n");
3443 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3444 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3445 si.nPage, si.nMin, si.nMax);
3446
3447 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3448 memset(&si, 0, sizeof(si));
3449 si.cbSize = sizeof(si);
3450 si.fMask = SIF_PAGE | SIF_RANGE;
3451 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3452 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3453 "Vertical scrollbar is visible, should be invisible.\n");
3454 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3455 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3456 si.nPage, si.nMin, si.nMax);
3457
3458 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3459 memset(&si, 0, sizeof(si));
3460 si.cbSize = sizeof(si);
3461 si.fMask = SIF_PAGE | SIF_RANGE;
3462 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3463 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3464 "Vertical scrollbar is invisible, should be visible.\n");
3465 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3466 "reported page/range is %d (%d..%d)\n",
3467 si.nPage, si.nMin, si.nMax);
3468
3469 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3470 DISABLE_WS_VSCROLL(hwndRichEdit);
3471 memset(&si, 0, sizeof(si));
3472 si.cbSize = sizeof(si);
3473 si.fMask = SIF_PAGE | SIF_RANGE;
3474 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3475 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3476 "Vertical scrollbar is visible, should be invisible.\n");
3477 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3478 "reported page/range is %d (%d..%d)\n",
3479 si.nPage, si.nMin, si.nMax);
3480
3481 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3482 memset(&si, 0, sizeof(si));
3483 si.cbSize = sizeof(si);
3484 si.fMask = SIF_PAGE | SIF_RANGE;
3485 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3486 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3487 "Vertical scrollbar is visible, should be invisible.\n");
3488 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3489 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3490 si.nPage, si.nMin, si.nMax);
3491
3492 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3493 memset(&si, 0, sizeof(si));
3494 si.cbSize = sizeof(si);
3495 si.fMask = SIF_PAGE | SIF_RANGE;
3496 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3497 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3498 "Vertical scrollbar is invisible, should be visible.\n");
3499 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3500 "reported page/range is %d (%d..%d)\n",
3501 si.nPage, si.nMin, si.nMax);
3502
3503 DISABLE_WS_VSCROLL(hwndRichEdit);
3504 memset(&si, 0, sizeof(si));
3505 si.cbSize = sizeof(si);
3506 si.fMask = SIF_PAGE | SIF_RANGE;
3507 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3508 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3509 "Vertical scrollbar is visible, should be invisible.\n");
3510 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3511 "reported page/range is %d (%d..%d)\n",
3512 si.nPage, si.nMin, si.nMax);
3513
3514 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3515 EM_SCROLL will make visible any forcefully invisible scrollbar */
3516 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3517 memset(&si, 0, sizeof(si));
3518 si.cbSize = sizeof(si);
3519 si.fMask = SIF_PAGE | SIF_RANGE;
3520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3522 "Vertical scrollbar is invisible, should be visible.\n");
3523 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3524 "reported page/range is %d (%d..%d)\n",
3525 si.nPage, si.nMin, si.nMax);
3526
3527 DISABLE_WS_VSCROLL(hwndRichEdit);
3528 memset(&si, 0, sizeof(si));
3529 si.cbSize = sizeof(si);
3530 si.fMask = SIF_PAGE | SIF_RANGE;
3531 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3532 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3533 "Vertical scrollbar is visible, should be invisible.\n");
3534 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3535 "reported page/range is %d (%d..%d)\n",
3536 si.nPage, si.nMin, si.nMax);
3537
3538 /* Again, EM_SCROLL, with SB_LINEUP */
3539 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3540 memset(&si, 0, sizeof(si));
3541 si.cbSize = sizeof(si);
3542 si.fMask = SIF_PAGE | SIF_RANGE;
3543 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3544 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3545 "Vertical scrollbar is invisible, should be visible.\n");
3546 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3547 "reported page/range is %d (%d..%d)\n",
3548 si.nPage, si.nMin, si.nMax);
3549
3550 DestroyWindow(hwndRichEdit);
3551
3552 /* This window proc models what is going on with Corman Lisp 3.0.
3553 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3554 force the scrollbar into visibility. Recursion should NOT happen
3555 as a result of this action.
3556 */
3557 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3558 if (r) {
3559 richeditProc = cls.lpfnWndProc;
3560 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3561 cls.lpszClassName = "RicheditStupidOverride";
3562 if(!RegisterClassA(&cls)) assert(0);
3563
3564 recursionLevel = 0;
3565 WM_SIZE_recursionLevel = 0;
3566 bailedOutOfRecursion = FALSE;
3567 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3568 ok(!bailedOutOfRecursion,
3569 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3570
3571 recursionLevel = 0;
3572 WM_SIZE_recursionLevel = 0;
3573 bailedOutOfRecursion = FALSE;
3574 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3575 ok(!bailedOutOfRecursion,
3576 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3577
3578 /* Unblock window in order to process WM_DESTROY */
3579 recursionLevel = 0;
3580 bailedOutOfRecursion = FALSE;
3581 WM_SIZE_recursionLevel = 0;
3582 DestroyWindow(hwndRichEdit);
3583 }
3584 }
3585
test_EM_SETUNDOLIMIT(void)3586 static void test_EM_SETUNDOLIMIT(void)
3587 {
3588 /* cases we test for:
3589 * default behaviour - limiting at 100 undo's
3590 * undo disabled - setting a limit of 0
3591 * undo limited - undo limit set to some to some number, like 2
3592 * bad input - sending a negative number should default to 100 undo's */
3593
3594 HWND hwndRichEdit = new_richedit(NULL);
3595 CHARRANGE cr;
3596 int i;
3597 int result;
3598
3599 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3600 cr.cpMin = 0;
3601 cr.cpMax = -1;
3602 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3603
3604 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3605 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3606 also, multiple pastes don't combine like WM_CHAR would */
3607
3608 /* first case - check the default */
3609 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3610 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3611 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3612 for (i=0; i<100; i++) /* Undo 100 of them */
3613 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3614 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3615 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3616
3617 /* second case - cannot undo */
3618 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3619 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3620 SendMessageA(hwndRichEdit,
3621 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3622 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3623 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3624
3625 /* third case - set it to an arbitrary number */
3626 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3627 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3628 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3629 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3630 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3631 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3632 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3633 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3634 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3635 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3636 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3637 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3638 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3639 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3640
3641 /* fourth case - setting negative numbers should default to 100 undos */
3642 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3643 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3644 ok (result == 100,
3645 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3646
3647 DestroyWindow(hwndRichEdit);
3648 }
3649
test_ES_PASSWORD(void)3650 static void test_ES_PASSWORD(void)
3651 {
3652 /* This isn't hugely testable, so we're just going to run it through its paces */
3653
3654 HWND hwndRichEdit = new_richedit(NULL);
3655 WCHAR result;
3656
3657 /* First, check the default of a regular control */
3658 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3659 ok (result == 0,
3660 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3661
3662 /* Now, set it to something normal */
3663 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3664 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3665 ok (result == 120,
3666 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3667
3668 /* Now, set it to something odd */
3669 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3670 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3671 ok (result == 1234,
3672 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3673 DestroyWindow(hwndRichEdit);
3674 }
3675
3676 LONG streamout_written = 0;
3677
test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)3678 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3679 LPBYTE pbBuff,
3680 LONG cb,
3681 LONG *pcb)
3682 {
3683 char** str = (char**)dwCookie;
3684 *pcb = cb;
3685 if (*pcb > 0) {
3686 memcpy(*str, pbBuff, *pcb);
3687 *str += *pcb;
3688 }
3689 streamout_written = *pcb;
3690 return 0;
3691 }
3692
test_WM_SETTEXT(void)3693 static void test_WM_SETTEXT(void)
3694 {
3695 HWND hwndRichEdit = new_richedit(NULL);
3696 const char * TestItem1 = "TestSomeText";
3697 const char * TestItem2 = "TestSomeText\r";
3698 const char * TestItem2_after = "TestSomeText\r\n";
3699 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3700 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3701 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3702 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3703 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3704 const char * TestItem5_after = "TestSomeText TestSomeText";
3705 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3706 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3707 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3708 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3709
3710 const char rtftextA[] = "{\\rtf sometext}";
3711 const char urtftextA[] = "{\\urtf sometext}";
3712 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3713 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3714 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3715
3716 char buf[1024] = {0};
3717 WCHAR bufW[1024] = {0};
3718 LRESULT result;
3719
3720 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3721 any solitary \r to be converted to \r\n on return. Properly paired
3722 \r\n are not affected. It also shows that the special sequence \r\r\n
3723 gets converted to a single space.
3724 */
3725
3726 #define TEST_SETTEXT(a, b) \
3727 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3728 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3729 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3730 ok (result == lstrlenA(buf), \
3731 "WM_GETTEXT returned %ld instead of expected %u\n", \
3732 result, lstrlenA(buf)); \
3733 result = strcmp(b, buf); \
3734 ok(result == 0, \
3735 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3736
3737 TEST_SETTEXT(TestItem1, TestItem1)
3738 TEST_SETTEXT(TestItem2, TestItem2_after)
3739 TEST_SETTEXT(TestItem3, TestItem3_after)
3740 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3741 TEST_SETTEXT(TestItem4, TestItem4_after)
3742 TEST_SETTEXT(TestItem5, TestItem5_after)
3743 TEST_SETTEXT(TestItem6, TestItem6_after)
3744 TEST_SETTEXT(TestItem7, TestItem7_after)
3745
3746 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3747 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3748 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3749 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3750 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3751 DestroyWindow(hwndRichEdit);
3752 #undef TEST_SETTEXT
3753
3754 #define TEST_SETTEXTW(a, b) \
3755 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3756 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3757 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3758 ok (result == lstrlenW(bufW), \
3759 "WM_GETTEXT returned %ld instead of expected %u\n", \
3760 result, lstrlenW(bufW)); \
3761 result = lstrcmpW(b, bufW); \
3762 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3763
3764 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3765 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3766 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3767 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3768 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3769 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3770 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3771 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3772 DestroyWindow(hwndRichEdit);
3773 #undef TEST_SETTEXTW
3774
3775 /* Single-line richedit */
3776 hwndRichEdit = new_richedit_with_style(NULL, 0);
3777 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3778 ok(result == 1, "WM_SETTEXT returned %ld, expected 12\n", result);
3779 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3780 ok(result == 5, "WM_GETTEXT returned %ld, expected 5\n", result);
3781 ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3782 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3783 ok(result == 1, "WM_SETTEXT returned %ld, expected 1\n", result);
3784 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3785 ok(result == 3, "WM_GETTEXT returned %ld, expected 3\n", result);
3786 ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3787 DestroyWindow(hwndRichEdit);
3788 }
3789
3790 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3791 resent to the callkack. */
test_esCallback_written_1(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)3792 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3793 LPBYTE pbBuff,
3794 LONG cb,
3795 LONG *pcb)
3796 {
3797 char** str = (char**)dwCookie;
3798 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3799 *pcb = 0;
3800 if (cb > 0) {
3801 memcpy(*str, pbBuff, cb);
3802 *str += cb;
3803 *pcb = 1;
3804 }
3805 return 0;
3806 }
3807
count_pars(const char * buf)3808 static int count_pars(const char *buf)
3809 {
3810 const char *p = buf;
3811 int count = 0;
3812 while ((p = strstr( p, "\\par" )) != NULL)
3813 {
3814 if (!isalpha( p[4] ))
3815 count++;
3816 p++;
3817 }
3818 return count;
3819 }
3820
test_EM_STREAMOUT(void)3821 static void test_EM_STREAMOUT(void)
3822 {
3823 HWND hwndRichEdit = new_richedit(NULL);
3824 int r;
3825 EDITSTREAM es;
3826 char buf[1024] = {0};
3827 char * p;
3828 LRESULT result;
3829
3830 const char * TestItem1 = "TestSomeText";
3831 const char * TestItem2 = "TestSomeText\r";
3832 const char * TestItem3 = "TestSomeText\r\n";
3833
3834 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3835 p = buf;
3836 es.dwCookie = (DWORD_PTR)&p;
3837 es.dwError = 0;
3838 es.pfnCallback = test_WM_SETTEXT_esCallback;
3839 memset(buf, 0, sizeof(buf));
3840 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3841 r = strlen(buf);
3842 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3843 ok(strcmp(buf, TestItem1) == 0,
3844 "streamed text different, got %s\n", buf);
3845 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3846
3847 /* RTF mode writes the final end of para \r if it's part of the selection */
3848 p = buf;
3849 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3850 ok (count_pars(buf) == 1, "got %s\n", buf);
3851 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3852 p = buf;
3853 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3854 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3855 ok (count_pars(buf) == 0, "got %s\n", buf);
3856 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3857 p = buf;
3858 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3859 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3860 ok (count_pars(buf) == 1, "got %s\n", buf);
3861 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3862
3863 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3864 p = buf;
3865 es.dwCookie = (DWORD_PTR)&p;
3866 es.dwError = 0;
3867 es.pfnCallback = test_WM_SETTEXT_esCallback;
3868 memset(buf, 0, sizeof(buf));
3869 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3870 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3871 r = strlen(buf);
3872 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3873 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3874 ok(strcmp(buf, TestItem3) == 0,
3875 "streamed text different from, got %s\n", buf);
3876
3877 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3878 p = buf;
3879 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3880 ok (count_pars(buf) == 2, "got %s\n", buf);
3881 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3882 p = buf;
3883 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3884 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3885 ok (count_pars(buf) == 1, "got %s\n", buf);
3886 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3887 p = buf;
3888 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3889 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3890 ok (count_pars(buf) == 2, "got %s\n", buf);
3891 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3892
3893 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3894 p = buf;
3895 es.dwCookie = (DWORD_PTR)&p;
3896 es.dwError = 0;
3897 es.pfnCallback = test_WM_SETTEXT_esCallback;
3898 memset(buf, 0, sizeof(buf));
3899 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3900 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3901 r = strlen(buf);
3902 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3903 ok(strcmp(buf, TestItem3) == 0,
3904 "streamed text different, got %s\n", buf);
3905
3906 /* Use a callback that sets *pcb to one */
3907 p = buf;
3908 es.dwCookie = (DWORD_PTR)&p;
3909 es.dwError = 0;
3910 es.pfnCallback = test_esCallback_written_1;
3911 memset(buf, 0, sizeof(buf));
3912 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3913 r = strlen(buf);
3914 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3915 ok(strcmp(buf, TestItem3) == 0,
3916 "streamed text different, got %s\n", buf);
3917 ok(result == 0, "got %ld expected 0\n", result);
3918
3919
3920 DestroyWindow(hwndRichEdit);
3921 }
3922
test_EM_STREAMOUT_FONTTBL(void)3923 static void test_EM_STREAMOUT_FONTTBL(void)
3924 {
3925 HWND hwndRichEdit = new_richedit(NULL);
3926 EDITSTREAM es;
3927 char buf[1024] = {0};
3928 char * p;
3929 char * fontTbl;
3930 int brackCount;
3931
3932 const char * TestItem = "TestSomeText";
3933
3934 /* fills in the richedit control with some text */
3935 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3936
3937 /* streams out the text in rtf format */
3938 p = buf;
3939 es.dwCookie = (DWORD_PTR)&p;
3940 es.dwError = 0;
3941 es.pfnCallback = test_WM_SETTEXT_esCallback;
3942 memset(buf, 0, sizeof(buf));
3943 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3944
3945 /* scans for \fonttbl, error if not found */
3946 fontTbl = strstr(buf, "\\fonttbl");
3947 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3948 if(fontTbl)
3949 {
3950 /* scans for terminating closing bracket */
3951 brackCount = 1;
3952 while(*fontTbl && brackCount)
3953 {
3954 if(*fontTbl == '{')
3955 brackCount++;
3956 else if(*fontTbl == '}')
3957 brackCount--;
3958 fontTbl++;
3959 }
3960 /* checks whether closing bracket is ok */
3961 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3962 if(!brackCount)
3963 {
3964 /* char before closing fonttbl block should be a closed bracket */
3965 fontTbl -= 2;
3966 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3967
3968 /* char after fonttbl block should be a crlf */
3969 fontTbl += 2;
3970 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3971 }
3972 }
3973 DestroyWindow(hwndRichEdit);
3974 }
3975
test_EM_STREAMOUT_empty_para(void)3976 static void test_EM_STREAMOUT_empty_para(void)
3977 {
3978 HWND hwnd = new_richedit(NULL);
3979 char buf[1024], *p = buf;
3980 EDITSTREAM es;
3981
3982 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
3983
3984 memset(buf, 0, sizeof(buf));
3985 es.dwCookie = (DWORD_PTR)&p;
3986 es.dwError = 0;
3987 es.pfnCallback = test_WM_SETTEXT_esCallback;
3988
3989 SendMessageA(hwnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3990 ok((p = strstr(buf, "\\pard")) != NULL, "missing \\pard\n");
3991 ok(((p = strstr(p, "\\fs")) && isdigit(p[3])), "missing \\fs\n");
3992
3993 DestroyWindow(hwnd);
3994 }
3995
test_EM_SETTEXTEX(void)3996 static void test_EM_SETTEXTEX(void)
3997 {
3998 HWND hwndRichEdit, parent;
3999 SCROLLINFO si;
4000 int sel_start, sel_end;
4001 SETTEXTEX setText;
4002 GETTEXTEX getText;
4003 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4004 'S', 'o', 'm', 'e',
4005 'T', 'e', 'x', 't', 0};
4006 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
4007 't', 'S', 'o', 'm',
4008 'e', 'T', 'e', 'x',
4009 't', 't', 'S', 'o',
4010 'm', 'e', 'T', 'e',
4011 'x', 't', 0};
4012 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
4013 '\r','t','S','o','m','e','T','e','x','t',0};
4014 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4015 'S', 'o', 'm', 'e',
4016 'T', 'e', 'x', 't',
4017 '\r', 0};
4018 const char * TestItem2_after = "TestSomeText\r\n";
4019 WCHAR TestItem3[] = {'T', 'e', 's', 't',
4020 'S', 'o', 'm', 'e',
4021 'T', 'e', 'x', 't',
4022 '\r','\n','\r','\n', 0};
4023 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
4024 'S', 'o', 'm', 'e',
4025 'T', 'e', 'x', 't',
4026 '\n','\n', 0};
4027 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
4028 'S', 'o', 'm', 'e',
4029 'T', 'e', 'x', 't',
4030 '\r','\r', 0};
4031 WCHAR TestItem4[] = {'T', 'e', 's', 't',
4032 'S', 'o', 'm', 'e',
4033 'T', 'e', 'x', 't',
4034 '\r','\r','\n','\r',
4035 '\n', 0};
4036 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
4037 'S', 'o', 'm', 'e',
4038 'T', 'e', 'x', 't',
4039 ' ','\r', 0};
4040 #define MAX_BUF_LEN 1024
4041 WCHAR buf[MAX_BUF_LEN];
4042 char bufACP[MAX_BUF_LEN];
4043 char * p;
4044 int result;
4045 CHARRANGE cr;
4046 EDITSTREAM es;
4047 WNDCLASSA cls;
4048
4049 /* Test the scroll position with and without a parent window.
4050 *
4051 * For some reason the scroll position is 0 after EM_SETTEXTEX
4052 * with the ST_SELECTION flag only when the control has a parent
4053 * window, even though the selection is at the end. */
4054 cls.style = 0;
4055 cls.lpfnWndProc = DefWindowProcA;
4056 cls.cbClsExtra = 0;
4057 cls.cbWndExtra = 0;
4058 cls.hInstance = GetModuleHandleA(0);
4059 cls.hIcon = 0;
4060 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
4061 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4062 cls.lpszMenuName = NULL;
4063 cls.lpszClassName = "ParentTestClass";
4064 if(!RegisterClassA(&cls)) assert(0);
4065
4066 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4067 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4068 ok (parent != 0, "Failed to create parent window\n");
4069
4070 hwndRichEdit = CreateWindowExA(0,
4071 RICHEDIT_CLASS20A, NULL,
4072 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4073 0, 0, 200, 60, parent, NULL,
4074 hmoduleRichEdit, NULL);
4075
4076 setText.codepage = CP_ACP;
4077 setText.flags = ST_SELECTION;
4078 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4079 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4080 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4081 si.cbSize = sizeof(si);
4082 si.fMask = SIF_ALL;
4083 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4084 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4085 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4086 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4087 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4088
4089 DestroyWindow(parent);
4090
4091 /* Test without a parent window */
4092 hwndRichEdit = new_richedit(NULL);
4093 setText.codepage = CP_ACP;
4094 setText.flags = ST_SELECTION;
4095 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4096 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4097 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4098 si.cbSize = sizeof(si);
4099 si.fMask = SIF_ALL;
4100 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4101 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4102 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4103 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4104 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4105
4106 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4107 * but this time it is because the selection is at the beginning. */
4108 setText.codepage = CP_ACP;
4109 setText.flags = ST_DEFAULT;
4110 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4111 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4112 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4113 si.cbSize = sizeof(si);
4114 si.fMask = SIF_ALL;
4115 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4116 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4117 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4118 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4119 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4120
4121 setText.codepage = 1200; /* no constant for unicode */
4122 getText.codepage = 1200; /* no constant for unicode */
4123 getText.cb = MAX_BUF_LEN;
4124 getText.flags = GT_DEFAULT;
4125 getText.lpDefaultChar = NULL;
4126 getText.lpUsedDefChar = NULL;
4127
4128 setText.flags = 0;
4129 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4130 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4131 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4132 ok(lstrcmpW(buf, TestItem1) == 0,
4133 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4134
4135 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4136 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4137 */
4138 setText.codepage = 1200; /* no constant for unicode */
4139 getText.codepage = 1200; /* no constant for unicode */
4140 getText.cb = MAX_BUF_LEN;
4141 getText.flags = GT_DEFAULT;
4142 getText.lpDefaultChar = NULL;
4143 getText.lpUsedDefChar = NULL;
4144 setText.flags = 0;
4145 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4146 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4147 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4148 ok(lstrcmpW(buf, TestItem2) == 0,
4149 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4150
4151 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4152 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4153 ok(strcmp((const char *)buf, TestItem2_after) == 0,
4154 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4155
4156 /* Baseline test for just-enough buffer space for string */
4157 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4158 getText.codepage = 1200; /* no constant for unicode */
4159 getText.flags = GT_DEFAULT;
4160 getText.lpDefaultChar = NULL;
4161 getText.lpUsedDefChar = NULL;
4162 memset(buf, 0, sizeof(buf));
4163 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4164 ok(lstrcmpW(buf, TestItem2) == 0,
4165 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4166
4167 /* When there is enough space for one character, but not both, of the CRLF
4168 pair at the end of the string, the CR is not copied at all. That is,
4169 the caller must not see CRLF pairs truncated to CR at the end of the
4170 string.
4171 */
4172 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4173 getText.codepage = 1200; /* no constant for unicode */
4174 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
4175 getText.lpDefaultChar = NULL;
4176 getText.lpUsedDefChar = NULL;
4177 memset(buf, 0, sizeof(buf));
4178 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4179 ok(lstrcmpW(buf, TestItem1) == 0,
4180 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4181
4182
4183 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4184 setText.codepage = 1200; /* no constant for unicode */
4185 getText.codepage = 1200; /* no constant for unicode */
4186 getText.cb = MAX_BUF_LEN;
4187 getText.flags = GT_DEFAULT;
4188 getText.lpDefaultChar = NULL;
4189 getText.lpUsedDefChar = NULL;
4190 setText.flags = 0;
4191 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4192 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4193 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4194 ok(lstrcmpW(buf, TestItem3_after) == 0,
4195 "EM_SETTEXTEX did not convert properly\n");
4196
4197 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4198 setText.codepage = 1200; /* no constant for unicode */
4199 getText.codepage = 1200; /* no constant for unicode */
4200 getText.cb = MAX_BUF_LEN;
4201 getText.flags = GT_DEFAULT;
4202 getText.lpDefaultChar = NULL;
4203 getText.lpUsedDefChar = NULL;
4204 setText.flags = 0;
4205 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4206 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4207 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4208 ok(lstrcmpW(buf, TestItem3_after) == 0,
4209 "EM_SETTEXTEX did not convert properly\n");
4210
4211 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4212 setText.codepage = 1200; /* no constant for unicode */
4213 getText.codepage = 1200; /* no constant for unicode */
4214 getText.cb = MAX_BUF_LEN;
4215 getText.flags = GT_DEFAULT;
4216 getText.lpDefaultChar = NULL;
4217 getText.lpUsedDefChar = NULL;
4218 setText.flags = 0;
4219 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4220 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4221 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4222 ok(lstrcmpW(buf, TestItem4_after) == 0,
4223 "EM_SETTEXTEX did not convert properly\n");
4224
4225 /* !ST_SELECTION && Unicode && !\rtf */
4226 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4227 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4228
4229 ok (result == 1,
4230 "EM_SETTEXTEX returned %d, instead of 1\n",result);
4231 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4232
4233 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4234 setText.flags = 0;
4235 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4236 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4237 /* select some text */
4238 cr.cpMax = 1;
4239 cr.cpMin = 3;
4240 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4241 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4242 setText.flags = ST_SELECTION;
4243 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4244 ok(result == 0,
4245 "EM_SETTEXTEX with NULL lParam to replace selection"
4246 " with no text should return 0. Got %i\n",
4247 result);
4248
4249 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4250 setText.flags = 0;
4251 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4252 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4253 /* select some text */
4254 cr.cpMax = 1;
4255 cr.cpMin = 3;
4256 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4257 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4258 setText.flags = ST_SELECTION;
4259 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4260 /* get text */
4261 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4262 ok(result == lstrlenW(TestItem1),
4263 "EM_SETTEXTEX with NULL lParam to replace selection"
4264 " with no text should return 0. Got %i\n",
4265 result);
4266 ok(lstrlenW(buf) == 22,
4267 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4268 lstrlenW(buf) );
4269
4270 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4271 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4272 p = (char *)buf;
4273 es.dwCookie = (DWORD_PTR)&p;
4274 es.dwError = 0;
4275 es.pfnCallback = test_WM_SETTEXT_esCallback;
4276 memset(buf, 0, sizeof(buf));
4277 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4278 (WPARAM)(SF_RTF), (LPARAM)&es);
4279 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4280
4281 /* !ST_SELECTION && !Unicode && \rtf */
4282 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4283 getText.codepage = 1200; /* no constant for unicode */
4284 getText.cb = MAX_BUF_LEN;
4285 getText.flags = GT_DEFAULT;
4286 getText.lpDefaultChar = NULL;
4287 getText.lpUsedDefChar = NULL;
4288
4289 setText.flags = 0;
4290 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4291 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4292 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4293 ok(lstrcmpW(buf, TestItem1) == 0,
4294 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4295
4296 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4297 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4298 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4299 getText.codepage = CP_ACP;
4300 getText.cb = MAX_BUF_LEN;
4301 getText.flags = GT_DEFAULT;
4302 getText.lpDefaultChar = NULL;
4303 getText.lpUsedDefChar = NULL;
4304
4305 setText.flags = ST_SELECTION;
4306 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4307 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4308 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4309 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4310 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4311
4312 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4313 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4314 p = (char *)buf;
4315 es.dwCookie = (DWORD_PTR)&p;
4316 es.dwError = 0;
4317 es.pfnCallback = test_WM_SETTEXT_esCallback;
4318 memset(buf, 0, sizeof(buf));
4319 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4320 (WPARAM)(SF_RTF), (LPARAM)&es);
4321 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4322
4323 /* select some text */
4324 cr.cpMax = 1;
4325 cr.cpMin = 3;
4326 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4327
4328 /* ST_SELECTION && !Unicode && \rtf */
4329 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4330 getText.codepage = 1200; /* no constant for unicode */
4331 getText.cb = MAX_BUF_LEN;
4332 getText.flags = GT_DEFAULT;
4333 getText.lpDefaultChar = NULL;
4334 getText.lpUsedDefChar = NULL;
4335
4336 setText.flags = ST_SELECTION;
4337 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4338 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4339 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4340
4341 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4342 setText.codepage = 1200; /* no constant for unicode */
4343 getText.codepage = CP_ACP;
4344 getText.cb = MAX_BUF_LEN;
4345
4346 setText.flags = 0;
4347 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4348 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4349
4350 /* select some text */
4351 cr.cpMax = 1;
4352 cr.cpMin = 3;
4353 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4354
4355 /* ST_SELECTION && !Unicode && !\rtf */
4356 setText.codepage = CP_ACP;
4357 getText.codepage = 1200; /* no constant for unicode */
4358 getText.cb = MAX_BUF_LEN;
4359 getText.flags = GT_DEFAULT;
4360 getText.lpDefaultChar = NULL;
4361 getText.lpUsedDefChar = NULL;
4362
4363 setText.flags = ST_SELECTION;
4364 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4365 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4366 ok(lstrcmpW(buf, TestItem1alt) == 0,
4367 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4368 " using ST_SELECTION and non-Unicode\n");
4369
4370 /* Test setting text using rich text format */
4371 setText.flags = 0;
4372 setText.codepage = CP_ACP;
4373 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4374 getText.codepage = CP_ACP;
4375 getText.cb = MAX_BUF_LEN;
4376 getText.flags = GT_DEFAULT;
4377 getText.lpDefaultChar = NULL;
4378 getText.lpUsedDefChar = NULL;
4379 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4380 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4381
4382 setText.flags = 0;
4383 setText.codepage = CP_ACP;
4384 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4385 getText.codepage = CP_ACP;
4386 getText.cb = MAX_BUF_LEN;
4387 getText.flags = GT_DEFAULT;
4388 getText.lpDefaultChar = NULL;
4389 getText.lpUsedDefChar = NULL;
4390 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4391 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4392
4393 /* test for utf8 text with BOM */
4394 setText.flags = 0;
4395 setText.codepage = CP_ACP;
4396 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4397 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4398 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4399 result = strcmp(bufACP, "TestUTF8WithBOM");
4400 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4401
4402 setText.flags = 0;
4403 setText.codepage = CP_UTF8;
4404 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4405 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4406 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4407 result = strcmp(bufACP, "TestUTF8WithBOM");
4408 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4409
4410 /* Test multibyte character */
4411 if (!is_lang_japanese)
4412 skip("Skip multibyte character tests on non-Japanese platform\n");
4413 else
4414 {
4415 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4416 setText.flags = ST_SELECTION;
4417 setText.codepage = CP_ACP;
4418 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4419 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4420 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4421 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4422 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4423 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4424
4425 setText.flags = ST_DEFAULT;
4426 setText.codepage = CP_ACP;
4427 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4428 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4429 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4430 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4431 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4432 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4433
4434 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4435 setText.flags = ST_SELECTION;
4436 setText.codepage = CP_ACP;
4437 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4438 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4439 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4440 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4441 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4442 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4443 }
4444
4445 DestroyWindow(hwndRichEdit);
4446
4447 /* Single-line richedit */
4448 hwndRichEdit = new_richedit_with_style(NULL, 0);
4449 setText.flags = ST_DEFAULT;
4450 setText.codepage = CP_ACP;
4451 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"line1\r\nline2");
4452 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4453 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4454 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4455 ok(!strcmp(bufACP, "line1"), "EM_SETTEXTEX: Test single-line text: Result: %s\n", bufACP);
4456 DestroyWindow(hwndRichEdit);
4457 }
4458
test_EM_LIMITTEXT(void)4459 static void test_EM_LIMITTEXT(void)
4460 {
4461 int ret;
4462
4463 HWND hwndRichEdit = new_richedit(NULL);
4464
4465 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4466 * about setting the length to -1 for multiline edit controls doesn't happen.
4467 */
4468
4469 /* Don't check default gettextlimit case. That's done in other tests */
4470
4471 /* Set textlimit to 100 */
4472 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4473 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4474 ok (ret == 100,
4475 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4476
4477 /* Set textlimit to 0 */
4478 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4479 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4480 ok (ret == 65536,
4481 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4482
4483 /* Set textlimit to -1 */
4484 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4485 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4486 ok (ret == -1,
4487 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4488
4489 /* Set textlimit to -2 */
4490 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4491 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4492 ok (ret == -2,
4493 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4494
4495 DestroyWindow (hwndRichEdit);
4496 }
4497
4498
test_EM_EXLIMITTEXT(void)4499 static void test_EM_EXLIMITTEXT(void)
4500 {
4501 int i, selBegin, selEnd, len1, len2;
4502 int result;
4503 char text[1024 + 1];
4504 char buffer[1024 + 1];
4505 int textlimit = 0; /* multiple of 100 */
4506 HWND hwndRichEdit = new_richedit(NULL);
4507
4508 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4509 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4510
4511 textlimit = 256000;
4512 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4513 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4514 /* set higher */
4515 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4516
4517 textlimit = 1000;
4518 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4519 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4520 /* set lower */
4521 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4522
4523 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4524 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4525 /* default for WParam = 0 */
4526 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4527
4528 textlimit = sizeof(text)-1;
4529 memset(text, 'W', textlimit);
4530 text[sizeof(text)-1] = 0;
4531 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4532 /* maxed out text */
4533 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4534
4535 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4536 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4537 len1 = selEnd - selBegin;
4538
4539 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4540 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4541 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4542 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4543 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4544 len2 = selEnd - selBegin;
4545
4546 ok(len1 != len2,
4547 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4548 len1,len2,i);
4549
4550 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4551 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4552 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4553 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4554 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4555 len1 = selEnd - selBegin;
4556
4557 ok(len1 != len2,
4558 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4559 len1,len2,i);
4560
4561 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4562 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4563 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4564 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4565 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4566 len2 = selEnd - selBegin;
4567
4568 ok(len1 == len2,
4569 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4570 len1,len2,i);
4571
4572 /* set text up to the limit, select all the text, then add a char */
4573 textlimit = 5;
4574 memset(text, 'W', textlimit);
4575 text[textlimit] = 0;
4576 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4577 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4578 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4579 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4580 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4581 result = strcmp(buffer, "A");
4582 ok(0 == result, "got string = \"%s\"\n", buffer);
4583
4584 /* WM_SETTEXT not limited */
4585 textlimit = 10;
4586 memset(text, 'W', textlimit);
4587 text[textlimit] = 0;
4588 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4589 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4590 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4591 i = strlen(buffer);
4592 ok(10 == i, "expected 10 chars\n");
4593 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4594 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4595
4596 /* try inserting more text at end */
4597 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4598 ok(0 == i, "WM_CHAR wasn't processed\n");
4599 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4600 i = strlen(buffer);
4601 ok(10 == i, "expected 10 chars, got %i\n", i);
4602 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4603 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4604
4605 /* try inserting text at beginning */
4606 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4607 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4608 ok(0 == i, "WM_CHAR wasn't processed\n");
4609 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4610 i = strlen(buffer);
4611 ok(10 == i, "expected 10 chars, got %i\n", i);
4612 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4613 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4614
4615 /* WM_CHAR is limited */
4616 textlimit = 1;
4617 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4618 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4619 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4620 ok(0 == i, "WM_CHAR wasn't processed\n");
4621 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4622 ok(0 == i, "WM_CHAR wasn't processed\n");
4623 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4624 i = strlen(buffer);
4625 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4626
4627 DestroyWindow(hwndRichEdit);
4628 }
4629
test_EM_GETLIMITTEXT(void)4630 static void test_EM_GETLIMITTEXT(void)
4631 {
4632 int i;
4633 HWND hwndRichEdit = new_richedit(NULL);
4634
4635 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4636 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4637
4638 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4639 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4640 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4641
4642 DestroyWindow(hwndRichEdit);
4643 }
4644
test_WM_SETFONT(void)4645 static void test_WM_SETFONT(void)
4646 {
4647 /* There is no invalid input or error conditions for this function.
4648 * NULL wParam and lParam just fall back to their default values
4649 * It should be noted that even if you use a gibberish name for your fonts
4650 * here, it will still work because the name is stored. They will display as
4651 * System, but will report their name to be whatever they were created as */
4652
4653 HWND hwndRichEdit = new_richedit(NULL);
4654 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4655 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4656 FF_DONTCARE, "Marlett");
4657 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4658 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4659 FF_DONTCARE, "MS Sans Serif");
4660 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4661 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4662 FF_DONTCARE, "Courier");
4663 LOGFONTA sentLogFont;
4664 CHARFORMAT2A returnedCF2A;
4665
4666 returnedCF2A.cbSize = sizeof(returnedCF2A);
4667
4668 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4669 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4670 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4671
4672 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4673 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4674 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4675 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4676
4677 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4678 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4679 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4680 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4681 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4682 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4683
4684 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4685 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4686 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4687 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4688 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4689 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4690
4691 /* This last test is special since we send in NULL. We clear the variables
4692 * and just compare to "System" instead of the sent in font name. */
4693 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4694 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4695 returnedCF2A.cbSize = sizeof(returnedCF2A);
4696
4697 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4698 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4699 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4700 ok (!strcmp("System",returnedCF2A.szFaceName),
4701 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4702
4703 DestroyWindow(hwndRichEdit);
4704 }
4705
4706
test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)4707 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4708 LPBYTE pbBuff,
4709 LONG cb,
4710 LONG *pcb)
4711 {
4712 const char** str = (const char**)dwCookie;
4713 int size = strlen(*str);
4714 if(size > 3) /* let's make it piecemeal for fun */
4715 size = 3;
4716 *pcb = cb;
4717 if (*pcb > size) {
4718 *pcb = size;
4719 }
4720 if (*pcb > 0) {
4721 memcpy(pbBuff, *str, *pcb);
4722 *str += *pcb;
4723 }
4724 return 0;
4725 }
4726
test_EM_GETMODIFY(void)4727 static void test_EM_GETMODIFY(void)
4728 {
4729 HWND hwndRichEdit = new_richedit(NULL);
4730 LRESULT result;
4731 SETTEXTEX setText;
4732 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4733 'S', 'o', 'm', 'e',
4734 'T', 'e', 'x', 't', 0};
4735 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4736 'S', 'o', 'm', 'e',
4737 'O', 't', 'h', 'e', 'r',
4738 'T', 'e', 'x', 't', 0};
4739 const char* streamText = "hello world";
4740 CHARFORMAT2A cf2;
4741 PARAFORMAT2 pf2;
4742 EDITSTREAM es;
4743
4744 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4745 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4746 FF_DONTCARE, "Courier");
4747
4748 setText.codepage = 1200; /* no constant for unicode */
4749 setText.flags = ST_KEEPUNDO;
4750
4751
4752 /* modify flag shouldn't be set when richedit is first created */
4753 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4754 ok (result == 0,
4755 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4756
4757 /* setting modify flag should actually set it */
4758 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4759 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4760 ok (result != 0,
4761 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4762
4763 /* clearing modify flag should actually clear it */
4764 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4765 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4766 ok (result == 0,
4767 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4768
4769 /* setting font doesn't change modify flag */
4770 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4771 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4772 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4773 ok (result == 0,
4774 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4775
4776 /* setting text should set modify flag */
4777 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4778 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4779 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4780 ok (result != 0,
4781 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4782
4783 /* undo previous text doesn't reset modify flag */
4784 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4785 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4786 ok (result != 0,
4787 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4788
4789 /* set text with no flag to keep undo stack should not set modify flag */
4790 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4791 setText.flags = 0;
4792 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4793 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4794 ok (result == 0,
4795 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4796
4797 /* WM_SETTEXT doesn't modify */
4798 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4799 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4800 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4801 ok (result == 0,
4802 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4803
4804 /* clear the text */
4805 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4806 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4807 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4808 ok (result == 0,
4809 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4810
4811 /* replace text */
4812 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4813 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4814 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4815 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4816 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4817 ok (result != 0,
4818 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4819
4820 /* copy/paste text 1 */
4821 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4822 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4823 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4824 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4825 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4826 ok (result != 0,
4827 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4828
4829 /* copy/paste text 2 */
4830 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4831 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4832 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4833 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4834 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4835 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4836 ok (result != 0,
4837 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4838
4839 /* press char */
4840 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4841 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4842 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4843 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4844 ok (result != 0,
4845 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4846
4847 /* press del */
4848 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4849 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4850 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4851 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4852 ok (result != 0,
4853 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4854
4855 /* set char format */
4856 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4857 cf2.cbSize = sizeof(CHARFORMAT2A);
4858 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4859 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4860 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4861 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4862 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4863 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4864 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4865 ok (result != 0,
4866 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4867
4868 /* set para format */
4869 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4870 pf2.cbSize = sizeof(PARAFORMAT2);
4871 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4872 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4873 pf2.wAlignment = PFA_RIGHT;
4874 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4875 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4876 ok (result == 0,
4877 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4878
4879 /* EM_STREAM */
4880 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4881 es.dwCookie = (DWORD_PTR)&streamText;
4882 es.dwError = 0;
4883 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4884 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4885 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4886 ok (result != 0,
4887 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4888
4889 DestroyWindow(hwndRichEdit);
4890 }
4891
4892 struct exsetsel_s {
4893 LONG min;
4894 LONG max;
4895 LRESULT expected_retval;
4896 int expected_getsel_start;
4897 int expected_getsel_end;
4898 BOOL todo;
4899 };
4900
4901 static const struct exsetsel_s exsetsel_tests[] = {
4902 /* sanity tests */
4903 {5, 10, 10, 5, 10 },
4904 {15, 17, 17, 15, 17 },
4905 /* test cpMax > strlen() */
4906 {0, 100, 18, 0, 18 },
4907 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4908 {-1, 1, 17, 17, 17 },
4909 /* test cpMin == cpMax */
4910 {5, 5, 5, 5, 5 },
4911 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4912 {-1, 0, 5, 5, 5 },
4913 {-1, 17, 5, 5, 5 },
4914 {-1, 18, 5, 5, 5 },
4915 /* test cpMin < 0 && cpMax < 0 */
4916 {-1, -1, 17, 17, 17 },
4917 {-4, -5, 17, 17, 17 },
4918 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4919 {0, -1, 18, 0, 18 },
4920 {17, -5, 18, 17, 18 },
4921 {18, -3, 17, 17, 17 },
4922 /* test if cpMin > cpMax */
4923 {15, 19, 18, 15, 18 },
4924 {19, 15, 18, 15, 18 },
4925 /* cpMin == strlen() && cpMax > cpMin */
4926 {17, 18, 18, 17, 18 },
4927 {17, 50, 18, 17, 18 },
4928 };
4929
check_EM_EXSETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)4930 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4931 CHARRANGE cr;
4932 LRESULT result;
4933 int start, end;
4934
4935 cr.cpMin = setsel->min;
4936 cr.cpMax = setsel->max;
4937 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4938
4939 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4940
4941 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4942
4943 todo_wine_if (setsel->todo)
4944 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4945 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4946 }
4947
test_EM_EXSETSEL(void)4948 static void test_EM_EXSETSEL(void)
4949 {
4950 HWND hwndRichEdit = new_richedit(NULL);
4951 int i;
4952 const int num_tests = ARRAY_SIZE(exsetsel_tests);
4953
4954 /* sending some text to the window */
4955 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4956 /* 01234567890123456*/
4957 /* 10 */
4958
4959 for (i = 0; i < num_tests; i++) {
4960 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4961 }
4962
4963 if (!is_lang_japanese)
4964 skip("Skip multibyte character tests on non-Japanese platform\n");
4965 else
4966 {
4967 CHARRANGE cr;
4968 char bufA[MAX_BUF_LEN] = {0};
4969 LRESULT result;
4970
4971 /* Test with multibyte character */
4972 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4973 /* 012345 6 78901 */
4974 cr.cpMin = 4; cr.cpMax = 8;
4975 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4976 ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4977 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4978 ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4979 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4980 ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4981 ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4982 }
4983
4984 DestroyWindow(hwndRichEdit);
4985 }
4986
check_EM_SETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)4987 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4988 LRESULT result;
4989 int start, end;
4990
4991 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4992
4993 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4994
4995 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4996
4997 todo_wine_if (setsel->todo)
4998 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4999 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
5000 }
5001
test_EM_SETSEL(void)5002 static void test_EM_SETSEL(void)
5003 {
5004 char buffA[32] = {0};
5005 HWND hwndRichEdit = new_richedit(NULL);
5006 int i;
5007 const int num_tests = ARRAY_SIZE(exsetsel_tests);
5008
5009 /* sending some text to the window */
5010 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5011 /* 01234567890123456*/
5012 /* 10 */
5013
5014 for (i = 0; i < num_tests; i++) {
5015 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
5016 }
5017
5018 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
5019 buffA[0] = 123;
5020 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5021 ok(buffA[0] == 0, "selection text %s\n", buffA);
5022
5023 if (!is_lang_japanese)
5024 skip("Skip multibyte character tests on non-Japanese platform\n");
5025 else
5026 {
5027 int sel_start, sel_end;
5028 LRESULT result;
5029
5030 /* Test with multibyte character */
5031 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5032 /* 012345 6 78901 */
5033 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
5034 ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
5035 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
5036 ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5037 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5038 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
5039 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
5040 }
5041
5042 DestroyWindow(hwndRichEdit);
5043 }
5044
test_EM_REPLACESEL(int redraw)5045 static void test_EM_REPLACESEL(int redraw)
5046 {
5047 HWND hwndRichEdit = new_richedit(NULL);
5048 char buffer[1024] = {0};
5049 int r;
5050 GETTEXTEX getText;
5051 CHARRANGE cr;
5052 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
5053 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
5054
5055 /* sending some text to the window */
5056 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5057 /* 01234567890123456*/
5058 /* 10 */
5059
5060 /* FIXME add more tests */
5061 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
5062 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
5063 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
5064 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5065 r = strcmp(buffer, "testing");
5066 ok(0 == r, "expected %d, got %d\n", 0, r);
5067
5068 DestroyWindow(hwndRichEdit);
5069
5070 hwndRichEdit = new_richedit(NULL);
5071
5072 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5073 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5074
5075 /* Test behavior with carriage returns and newlines */
5076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5077 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5078 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5079 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5080 r = strcmp(buffer, "RichEdit1");
5081 ok(0 == r, "expected %d, got %d\n", 0, r);
5082 getText.cb = 1024;
5083 getText.codepage = CP_ACP;
5084 getText.flags = GT_DEFAULT;
5085 getText.lpDefaultChar = NULL;
5086 getText.lpUsedDefChar = NULL;
5087 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5088 ok(strcmp(buffer, "RichEdit1") == 0,
5089 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5090
5091 /* Test number of lines reported after EM_REPLACESEL */
5092 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5093 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5094
5095 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5096 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5097 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5098 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5099 r = strcmp(buffer, "RichEdit1\r\n");
5100 ok(0 == r, "expected %d, got %d\n", 0, r);
5101 getText.cb = 1024;
5102 getText.codepage = CP_ACP;
5103 getText.flags = GT_DEFAULT;
5104 getText.lpDefaultChar = NULL;
5105 getText.lpUsedDefChar = NULL;
5106 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5107 ok(strcmp(buffer, "RichEdit1\r") == 0,
5108 "EM_GETTEXTEX returned incorrect string\n");
5109
5110 /* Test number of lines reported after EM_REPLACESEL */
5111 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5112 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5113
5114 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5115 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5116 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5117
5118 /* Test number of lines reported after EM_REPLACESEL */
5119 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5120 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5121
5122 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5123 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5124 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5125 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5126
5127 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5128 r = strcmp(buffer, "RichEdit1\r\n");
5129 ok(0 == r, "expected %d, got %d\n", 0, r);
5130 getText.cb = 1024;
5131 getText.codepage = CP_ACP;
5132 getText.flags = GT_DEFAULT;
5133 getText.lpDefaultChar = NULL;
5134 getText.lpUsedDefChar = NULL;
5135 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5136 ok(strcmp(buffer, "RichEdit1\r") == 0,
5137 "EM_GETTEXTEX returned incorrect string\n");
5138
5139 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5140 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5141 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5142 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5143
5144 /* The following tests show that richedit should handle the special \r\r\n
5145 sequence by turning it into a single space on insertion. However,
5146 EM_REPLACESEL on WinXP returns the number of characters in the original
5147 string.
5148 */
5149
5150 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5151 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5152 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5153 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5154 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5155 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5156 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5157
5158 /* Test the actual string */
5159 getText.cb = 1024;
5160 getText.codepage = CP_ACP;
5161 getText.flags = GT_DEFAULT;
5162 getText.lpDefaultChar = NULL;
5163 getText.lpUsedDefChar = NULL;
5164 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5165 ok(strcmp(buffer, "\r\r") == 0,
5166 "EM_GETTEXTEX returned incorrect string\n");
5167
5168 /* Test number of lines reported after EM_REPLACESEL */
5169 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5170 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5171
5172 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5173 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5174 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5175 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5176 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5177 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
5178 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
5179
5180 /* Test the actual string */
5181 getText.cb = 1024;
5182 getText.codepage = CP_ACP;
5183 getText.flags = GT_DEFAULT;
5184 getText.lpDefaultChar = NULL;
5185 getText.lpUsedDefChar = NULL;
5186 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5187 ok(strcmp(buffer, " ") == 0,
5188 "EM_GETTEXTEX returned incorrect string\n");
5189
5190 /* Test number of lines reported after EM_REPLACESEL */
5191 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5192 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5193
5194 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5195 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5196 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5197 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5198 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5199 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5200 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5201
5202 /* Test the actual string */
5203 getText.cb = 1024;
5204 getText.codepage = CP_ACP;
5205 getText.flags = GT_DEFAULT;
5206 getText.lpDefaultChar = NULL;
5207 getText.lpUsedDefChar = NULL;
5208 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5209 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5210 "EM_GETTEXTEX returned incorrect string\n");
5211
5212 /* Test number of lines reported after EM_REPLACESEL */
5213 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5214 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5215
5216 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5217 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5218 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5219 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5220 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5221 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5222 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5223
5224 /* Test the actual string */
5225 getText.cb = 1024;
5226 getText.codepage = CP_ACP;
5227 getText.flags = GT_DEFAULT;
5228 getText.lpDefaultChar = NULL;
5229 getText.lpUsedDefChar = NULL;
5230 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5231 ok(strcmp(buffer, " \r") == 0,
5232 "EM_GETTEXTEX returned incorrect string\n");
5233
5234 /* Test number of lines reported after EM_REPLACESEL */
5235 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5236 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5237
5238 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5239 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5240 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5241 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5242 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5243 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
5244 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
5245
5246 /* Test the actual string */
5247 getText.cb = 1024;
5248 getText.codepage = CP_ACP;
5249 getText.flags = GT_DEFAULT;
5250 getText.lpDefaultChar = NULL;
5251 getText.lpUsedDefChar = NULL;
5252 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5253 ok(strcmp(buffer, " \r\r") == 0,
5254 "EM_GETTEXTEX returned incorrect string\n");
5255
5256 /* Test number of lines reported after EM_REPLACESEL */
5257 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5258 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5259
5260 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5261 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5262 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5263 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5264 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5265 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
5266 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
5267
5268 /* Test the actual string */
5269 getText.cb = 1024;
5270 getText.codepage = CP_ACP;
5271 getText.flags = GT_DEFAULT;
5272 getText.lpDefaultChar = NULL;
5273 getText.lpUsedDefChar = NULL;
5274 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5275 ok(strcmp(buffer, "\rX\r\r\r") == 0,
5276 "EM_GETTEXTEX returned incorrect string\n");
5277
5278 /* Test number of lines reported after EM_REPLACESEL */
5279 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5280 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5281
5282 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5283 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5284 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5285 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5286 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5287 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5288 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5289
5290 /* Test the actual string */
5291 getText.cb = 1024;
5292 getText.codepage = CP_ACP;
5293 getText.flags = GT_DEFAULT;
5294 getText.lpDefaultChar = NULL;
5295 getText.lpUsedDefChar = NULL;
5296 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5297 ok(strcmp(buffer, "\r\r") == 0,
5298 "EM_GETTEXTEX returned incorrect string\n");
5299
5300 /* Test number of lines reported after EM_REPLACESEL */
5301 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5302 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5303
5304 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5305 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5306 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5307 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5308 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5309 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5310 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5311
5312 /* Test the actual string */
5313 getText.cb = 1024;
5314 getText.codepage = CP_ACP;
5315 getText.flags = GT_DEFAULT;
5316 getText.lpDefaultChar = NULL;
5317 getText.lpUsedDefChar = NULL;
5318 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5319 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5320 "EM_GETTEXTEX returned incorrect string\n");
5321
5322 /* Test number of lines reported after EM_REPLACESEL */
5323 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5324 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5325
5326 /* Test with multibyte character */
5327 if (!is_lang_japanese)
5328 skip("Skip multibyte character tests on non-Japanese platform\n");
5329 else
5330 {
5331 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5332 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5333 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5334 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5335 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5336 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5337 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5338 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5339 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5340 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5341
5342 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5343 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5344 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5345 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5346 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5347 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5348 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5349 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5350 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5351 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5352 }
5353
5354 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5355 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5356 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5357 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5358 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5359 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5360 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5361 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5362 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5363
5364 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5365 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5366 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5367 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5368 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5369 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5370 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5371 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5372 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5373
5374 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5375 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5376 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5377 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5378 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5379 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5380 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5381 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5382 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5383 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5384
5385 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5386 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5387 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5388 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5389 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5390 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5391 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5392 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5393 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5394 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5395
5396 if (!redraw)
5397 /* This is needed to avoid interfering with keybd_event calls
5398 * on other tests that simulate keyboard events. */
5399 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5400
5401 DestroyWindow(hwndRichEdit);
5402
5403 /* Single-line richedit */
5404 hwndRichEdit = new_richedit_with_style(NULL, 0);
5405 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"line1\r\nline2");
5406 ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5407 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5408 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5409 ok(!strcmp(buffer, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buffer);
5410 DestroyWindow(hwndRichEdit);
5411 }
5412
5413 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5414 * to test the state of the modifiers (Ctrl/Alt/Shift).
5415 *
5416 * Therefore Ctrl-<key> keystrokes need to be simulated with
5417 * keybd_event or by using SetKeyboardState to set the modifiers
5418 * and SendMessage to simulate the keystrokes.
5419 */
send_ctrl_key(HWND hwnd,UINT key)5420 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5421 {
5422 LRESULT result;
5423 hold_key(VK_CONTROL);
5424 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5425 release_key(VK_CONTROL);
5426 return result;
5427 }
5428
test_WM_PASTE(void)5429 static void test_WM_PASTE(void)
5430 {
5431 int result;
5432 char buffer[1024] = {0};
5433 const char* text1 = "testing paste\r";
5434 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5435 const char* text1_after = "testing paste\r\n";
5436 const char* text2 = "testing paste\r\rtesting paste";
5437 const char* text2_after = "testing paste\r\n\r\ntesting paste";
5438 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5439 HWND hwndRichEdit = new_richedit(NULL);
5440
5441 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5442 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5443
5444 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5445 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5446 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5447 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5448 /* Pasted text should be visible at this step */
5449 result = strcmp(text1_step1, buffer);
5450 ok(result == 0,
5451 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5452
5453 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5454 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5455 /* Text should be the same as before (except for \r -> \r\n conversion) */
5456 result = strcmp(text1_after, buffer);
5457 ok(result == 0,
5458 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5459
5460 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5461 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5462 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5463 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5464 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5465 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5466 /* Pasted text should be visible at this step */
5467 result = strcmp(text3, buffer);
5468 ok(result == 0,
5469 "test paste: strcmp = %i\n", result);
5470 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5471 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5472 /* Text should be the same as before (except for \r -> \r\n conversion) */
5473 result = strcmp(text2_after, buffer);
5474 ok(result == 0,
5475 "test paste: strcmp = %i\n", result);
5476 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5477 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5478 /* Text should revert to post-paste state */
5479 result = strcmp(buffer,text3);
5480 ok(result == 0,
5481 "test paste: strcmp = %i\n", result);
5482
5483 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5484 /* Send WM_CHAR to simulate Ctrl-V */
5485 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5486 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5487 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5488 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5489 result = strcmp(buffer,"");
5490 ok(result == 0,
5491 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5492
5493 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5494 * with SetKeyboard state. */
5495
5496 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5497 /* Simulates paste (Ctrl-V) */
5498 hold_key(VK_CONTROL);
5499 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5500 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5501 release_key(VK_CONTROL);
5502 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5503 result = strcmp(buffer,"paste");
5504 ok(result == 0,
5505 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5506
5507 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5508 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5509 /* Simulates copy (Ctrl-C) */
5510 hold_key(VK_CONTROL);
5511 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5512 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5513 release_key(VK_CONTROL);
5514 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5515 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5516 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5517 result = strcmp(buffer,"testing");
5518 ok(result == 0,
5519 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5520
5521 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5522 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5523 /* Simulates select all (Ctrl-A) */
5524 hold_key(VK_CONTROL);
5525 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5526 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5527 /* Simulates select cut (Ctrl-X) */
5528 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5529 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5530 release_key(VK_CONTROL);
5531 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5532 result = strcmp(buffer,"");
5533 ok(result == 0,
5534 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5535 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5536 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5537 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5538 result = strcmp(buffer,"cut\r\n");
5539 ok(result == 0,
5540 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5541 /* Simulates undo (Ctrl-Z) */
5542 hold_key(VK_CONTROL);
5543 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5544 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5545 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5546 result = strcmp(buffer,"");
5547 ok(result == 0,
5548 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5549 /* Simulates redo (Ctrl-Y) */
5550 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5551 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5552 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5553 result = strcmp(buffer,"cut\r\n");
5554 ok(result == 0,
5555 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5556 release_key(VK_CONTROL);
5557
5558 /* Copy multiline text to clipboard for future use */
5559 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
5560 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5561 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5562 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
5563
5564 /* Paste into read-only control */
5565 result = SendMessageA(hwndRichEdit, EM_SETREADONLY, TRUE, 0);
5566 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5567 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5568 result = strcmp(buffer, text3);
5569 ok(result == 0,
5570 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5571
5572 /* Cut from read-only control */
5573 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5574 SendMessageA(hwndRichEdit, WM_CUT, 0, 0);
5575 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5576 result = strcmp(buffer, text3);
5577 ok(result == 0,
5578 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5579
5580 /* FIXME: Wine doesn't flush Ole clipboard when window is destroyed so do it manually */
5581 OleFlushClipboard();
5582 DestroyWindow(hwndRichEdit);
5583
5584 /* Paste multi-line text into single-line control */
5585 hwndRichEdit = new_richedit_with_style(NULL, 0);
5586 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5587 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5588 result = strcmp(buffer, "testing paste");
5589 ok(result == 0,
5590 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5591 DestroyWindow(hwndRichEdit);
5592 }
5593
test_EM_FORMATRANGE(void)5594 static void test_EM_FORMATRANGE(void)
5595 {
5596 int r, i, tpp_x, tpp_y;
5597 HDC hdc;
5598 HWND hwndRichEdit = new_richedit(NULL);
5599 FORMATRANGE fr;
5600 BOOL skip_non_english;
5601 static const struct {
5602 const char *string; /* The string */
5603 int first; /* First 'pagebreak', 0 for don't care */
5604 int second; /* Second 'pagebreak', 0 for don't care */
5605 } fmtstrings[] = {
5606 {"WINE wine", 0, 0},
5607 {"WINE wineWine", 0, 0},
5608 {"WINE\r\nwine\r\nwine", 5, 10},
5609 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5610 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5611 };
5612
5613 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5614 if (skip_non_english)
5615 skip("Skipping some tests on non-English platform\n");
5616
5617 hdc = GetDC(hwndRichEdit);
5618 ok(hdc != NULL, "Could not get HDC\n");
5619
5620 /* Calculate the twips per pixel */
5621 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5622 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5623
5624 /* Test the simple case where all the text fits in the page rect. */
5625 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5626 fr.hdc = fr.hdcTarget = hdc;
5627 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5628 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5629 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5630 fr.chrg.cpMin = 0;
5631 fr.chrg.cpMax = -1;
5632 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5633 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5634
5635 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5636 fr.rc.bottom = fr.rcPage.bottom;
5637 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5638 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5639
5640 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5641
5642 for (i = 0; i < ARRAY_SIZE(fmtstrings); i++)
5643 {
5644 GETTEXTLENGTHEX gtl;
5645 SIZE stringsize;
5646 int len;
5647
5648 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5649
5650 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5651 gtl.codepage = CP_ACP;
5652 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5653
5654 /* Get some size information for the string */
5655 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5656
5657 /* Define the box to be half the width needed and a bit larger than the height.
5658 * Changes to the width means we have at least 2 pages. Changes to the height
5659 * is done so we can check the changing of fr.rc.bottom.
5660 */
5661 fr.hdc = fr.hdcTarget = hdc;
5662 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5663 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5664 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5665
5666 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5667 todo_wine {
5668 ok(r == len, "Expected %d, got %d\n", len, r);
5669 }
5670
5671 /* We know that the page can't hold the full string. See how many characters
5672 * are on the first one
5673 */
5674 fr.chrg.cpMin = 0;
5675 fr.chrg.cpMax = -1;
5676 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5677 todo_wine {
5678 if (! skip_non_english)
5679 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5680 }
5681 if (fmtstrings[i].first)
5682 todo_wine {
5683 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5684 }
5685 else
5686 ok(r < len, "Expected < %d, got %d\n", len, r);
5687
5688 /* Do another page */
5689 fr.chrg.cpMin = r;
5690 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5691 if (fmtstrings[i].second)
5692 todo_wine {
5693 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5694 }
5695 else if (! skip_non_english)
5696 ok (r < len, "Expected < %d, got %d\n", len, r);
5697
5698 /* There is at least on more page, but we don't care */
5699
5700 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5701 todo_wine {
5702 ok(r == len, "Expected %d, got %d\n", len, r);
5703 }
5704 }
5705
5706 ReleaseDC(NULL, hdc);
5707 DestroyWindow(hwndRichEdit);
5708 }
5709
5710 static int nCallbackCount = 0;
5711
EditStreamCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5712 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5713 LONG cb, LONG* pcb)
5714 {
5715 const char text[] = {'t','e','s','t'};
5716
5717 if (sizeof(text) <= cb)
5718 {
5719 if ((int)dwCookie != nCallbackCount)
5720 {
5721 *pcb = 0;
5722 return 0;
5723 }
5724
5725 memcpy (pbBuff, text, sizeof(text));
5726 *pcb = sizeof(text);
5727
5728 nCallbackCount++;
5729
5730 return 0;
5731 }
5732 else
5733 return 1; /* indicates callback failed */
5734 }
5735
test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5736 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5737 LPBYTE pbBuff,
5738 LONG cb,
5739 LONG *pcb)
5740 {
5741 const char** str = (const char**)dwCookie;
5742 int size = strlen(*str);
5743 *pcb = cb;
5744 if (*pcb > size) {
5745 *pcb = size;
5746 }
5747 if (*pcb > 0) {
5748 memcpy(pbBuff, *str, *pcb);
5749 *str += *pcb;
5750 }
5751 return 0;
5752 }
5753
test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5754 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5755 LPBYTE pbBuff,
5756 LONG cb,
5757 LONG *pcb)
5758 {
5759 DWORD *phase = (DWORD *)dwCookie;
5760
5761 if(*phase == 0){
5762 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5763 *pcb = sizeof(first) - 1;
5764 memcpy(pbBuff, first, *pcb);
5765 }else if(*phase == 1){
5766 static const char second[] = "\x8f\xc3\x8b";
5767 *pcb = sizeof(second) - 1;
5768 memcpy(pbBuff, second, *pcb);
5769 }else
5770 *pcb = 0;
5771
5772 ++*phase;
5773
5774 return 0;
5775 }
5776
test_EM_STREAMIN_null_bytes(DWORD_PTR cookie,BYTE * buf,LONG size,LONG * written)5777 static DWORD CALLBACK test_EM_STREAMIN_null_bytes(DWORD_PTR cookie, BYTE *buf, LONG size, LONG *written)
5778 {
5779 DWORD *phase = (DWORD *)cookie;
5780
5781 if (*phase == 0)
5782 {
5783 static const char first[] = "{\\rtf1\\ansi{Th\0is";
5784 *written = sizeof(first);
5785 memcpy(buf, first, *written);
5786 }
5787 else if (*phase == 1)
5788 {
5789 static const char second[] = " is a test}}";
5790 *written = sizeof(second);
5791 memcpy(buf, second, *written);
5792 }
5793 else
5794 *written = 0;
5795
5796 ++*phase;
5797
5798 return 0;
5799 }
5800
5801 struct StringWithLength {
5802 int length;
5803 char *buffer;
5804 };
5805
5806 /* This callback is used to handled the null characters in a string. */
test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5807 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5808 LPBYTE pbBuff,
5809 LONG cb,
5810 LONG *pcb)
5811 {
5812 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5813 int size = str->length;
5814 *pcb = cb;
5815 if (*pcb > size) {
5816 *pcb = size;
5817 }
5818 if (*pcb > 0) {
5819 memcpy(pbBuff, str->buffer, *pcb);
5820 str->buffer += *pcb;
5821 str->length -= *pcb;
5822 }
5823 return 0;
5824 }
5825
test_EM_STREAMIN(void)5826 static void test_EM_STREAMIN(void)
5827 {
5828 HWND hwndRichEdit = new_richedit(NULL);
5829 DWORD phase;
5830 LRESULT result;
5831 EDITSTREAM es;
5832 char buffer[1024] = {0}, tmp[16];
5833 CHARRANGE range;
5834 PARAFORMAT2 fmt;
5835
5836 const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5837 const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5838 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5839 const char * ptr;
5840
5841 const char * streamText1 =
5842 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5843 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5844 "}\r\n";
5845
5846 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5847 const char * streamText2 =
5848 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5849 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5850 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5851 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5852 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5853 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5854 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5855
5856 const char * streamText3 = "RichEdit1";
5857
5858 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5859
5860 const char * streamText4 =
5861 "This text just needs to be long enough to cause run to be split onto "
5862 "two separate lines and make sure the null terminating character is "
5863 "handled properly.\0";
5864
5865 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5866
5867 int length4 = strlen(streamText4) + 1;
5868 struct StringWithLength cookieForStream4 = {
5869 length4,
5870 (char *)streamText4,
5871 };
5872
5873 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5874 int length5 = ARRAY_SIZE(streamText5);
5875 struct StringWithLength cookieForStream5 = {
5876 sizeof(streamText5),
5877 (char *)streamText5,
5878 };
5879
5880 /* Minimal test without \par at the end */
5881 es.dwCookie = (DWORD_PTR)&streamText0;
5882 es.dwError = 0;
5883 es.pfnCallback = test_EM_STREAMIN_esCallback;
5884 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5885 ok(result == 12, "got %ld, expected %d\n", result, 12);
5886
5887 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5888 ok (result == 12,
5889 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5890 result = strcmp (buffer,"TestSomeText");
5891 ok (result == 0,
5892 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5893 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5894 /* Show that para fmts are ignored */
5895 range.cpMin = 2;
5896 range.cpMax = 2;
5897 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5898 memset(&fmt, 0xcc, sizeof(fmt));
5899 fmt.cbSize = sizeof(fmt);
5900 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5901 ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5902 ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5903 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5904 ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5905
5906 /* Native richedit 2.0 ignores last \par */
5907 ptr = streamText0a;
5908 es.dwCookie = (DWORD_PTR)&ptr;
5909 es.dwError = 0;
5910 es.pfnCallback = test_EM_STREAMIN_esCallback;
5911 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5912 ok(result == 12, "got %ld, expected %d\n", result, 12);
5913
5914 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5915 ok (result == 12,
5916 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5917 result = strcmp (buffer,"TestSomeText");
5918 ok (result == 0,
5919 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5920 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5921 /* This time para fmts are processed */
5922 range.cpMin = 2;
5923 range.cpMax = 2;
5924 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5925 memset(&fmt, 0xcc, sizeof(fmt));
5926 fmt.cbSize = sizeof(fmt);
5927 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5928 ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5929 ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5930 ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5931 ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5932
5933 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5934 es.dwCookie = (DWORD_PTR)&streamText0b;
5935 es.dwError = 0;
5936 es.pfnCallback = test_EM_STREAMIN_esCallback;
5937 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5938 ok(result == 13, "got %ld, expected %d\n", result, 13);
5939
5940 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5941 ok (result == 14,
5942 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5943 result = strcmp (buffer,"TestSomeText\r\n");
5944 ok (result == 0,
5945 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5946 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5947
5948 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5949 ptr = streamText0a;
5950 es.dwCookie = (DWORD_PTR)&ptr;
5951 es.dwError = 0;
5952 es.pfnCallback = test_EM_STREAMIN_esCallback;
5953 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5954 ok(result == 12, "got %ld, expected %d\n", result, 12);
5955
5956 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5957 ok (result == 12,
5958 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5959 result = strcmp (buffer,"TestSomeText");
5960 ok (result == 0,
5961 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5962 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5963
5964 range.cpMin = 0;
5965 range.cpMax = -1;
5966 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5967 ok (result == 13, "got %ld\n", result);
5968
5969 ptr = streamText0a;
5970 es.dwCookie = (DWORD_PTR)&ptr;
5971 es.dwError = 0;
5972 es.pfnCallback = test_EM_STREAMIN_esCallback;
5973
5974 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5975 ok(result == 13, "got %ld, expected 13\n", result);
5976
5977 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5978 ok (result == 14,
5979 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5980 result = strcmp (buffer,"TestSomeText\r\n");
5981 ok (result == 0,
5982 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5983 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5984
5985 es.dwCookie = (DWORD_PTR)&streamText1;
5986 es.dwError = 0;
5987 es.pfnCallback = test_EM_STREAMIN_esCallback;
5988 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5989 ok(result == 12, "got %ld, expected %d\n", result, 12);
5990
5991 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5992 ok (result == 12,
5993 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5994 result = strcmp (buffer,"TestSomeText");
5995 ok (result == 0,
5996 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5997 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5998
5999 es.dwCookie = (DWORD_PTR)&streamText2;
6000 es.dwError = 0;
6001 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6002 ok(result == 0, "got %ld, expected %d\n", result, 0);
6003
6004 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6005 ok (result == 0,
6006 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
6007 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6008 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
6009
6010 es.dwCookie = (DWORD_PTR)&streamText3;
6011 es.dwError = 0;
6012 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6013 ok(result == 0, "got %ld, expected %d\n", result, 0);
6014
6015 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6016 ok (result == 0,
6017 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
6018 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
6019 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
6020
6021 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
6022 es.dwError = 0;
6023 es.pfnCallback = test_EM_STREAMIN_esCallback;
6024 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6025 ok(result == 18, "got %ld, expected %d\n", result, 18);
6026
6027 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6028 ok(result == 15,
6029 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
6030 result = strcmp (buffer,"TestUTF8WithBOM");
6031 ok(result == 0,
6032 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
6033 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
6034
6035 phase = 0;
6036 es.dwCookie = (DWORD_PTR)&phase;
6037 es.dwError = 0;
6038 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
6039 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6040 ok(result == 8, "got %ld\n", result);
6041
6042 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
6043
6044 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6045 ok(result == 3,
6046 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
6047 result = memcmp (buffer, tmp, 3);
6048 ok(result == 0,
6049 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
6050 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
6051
6052 es.dwCookie = (DWORD_PTR)&cookieForStream4;
6053 es.dwError = 0;
6054 es.pfnCallback = test_EM_STREAMIN_esCallback2;
6055 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6056 ok(result == length4, "got %ld, expected %d\n", result, length4);
6057
6058 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6059 ok (result == length4,
6060 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
6061 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
6062
6063 es.dwCookie = (DWORD_PTR)&cookieForStream5;
6064 es.dwError = 0;
6065 es.pfnCallback = test_EM_STREAMIN_esCallback2;
6066 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
6067 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
6068
6069 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6070 ok (result == length5,
6071 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
6072 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
6073
6074 DestroyWindow(hwndRichEdit);
6075
6076 /* Single-line richedit */
6077 hwndRichEdit = new_richedit_with_style(NULL, 0);
6078 ptr = "line1\r\nline2";
6079 es.dwCookie = (DWORD_PTR)&ptr;
6080 es.dwError = 0;
6081 es.pfnCallback = test_EM_STREAMIN_esCallback;
6082 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6083 ok(result == 12, "got %ld, expected %d\n", result, 12);
6084 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6085 ok (!strcmp(buffer, "line1"),
6086 "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6087
6088 /* Test 0-bytes inside text */
6089 hwndRichEdit = new_richedit_with_style(NULL, 0);
6090 phase = 0;
6091 es.dwCookie = (DWORD_PTR)&phase;
6092 es.dwError = 0;
6093 es.pfnCallback = test_EM_STREAMIN_null_bytes;
6094 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6095 ok(result == 16, "got %ld, expected %d\n", result, 16);
6096 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6097 ok (!strcmp(buffer, "Th is is a test"), "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6098 }
6099
test_EM_StreamIn_Undo(void)6100 static void test_EM_StreamIn_Undo(void)
6101 {
6102 /* The purpose of this test is to determine when a EM_StreamIn should be
6103 * undoable. This is important because WM_PASTE currently uses StreamIn and
6104 * pasting should always be undoable but streaming isn't always.
6105 *
6106 * cases to test:
6107 * StreamIn plain text without SFF_SELECTION.
6108 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
6109 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
6110 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
6111 * Feel free to add tests for other text modes or StreamIn things.
6112 */
6113
6114
6115 HWND hwndRichEdit = new_richedit(NULL);
6116 LRESULT result;
6117 EDITSTREAM es;
6118 char buffer[1024] = {0};
6119 const char randomtext[] = "Some text";
6120
6121 es.pfnCallback = EditStreamCallback;
6122
6123 /* StreamIn, no SFF_SELECTION */
6124 es.dwCookie = nCallbackCount;
6125 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6126 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6127 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6128 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6129 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6130 result = strcmp (buffer,"test");
6131 ok (result == 0,
6132 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6133
6134 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6135 ok (result == FALSE,
6136 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
6137
6138 /* StreamIn, SFF_SELECTION, but nothing selected */
6139 es.dwCookie = nCallbackCount;
6140 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6141 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6142 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6143 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6144 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6145 result = strcmp (buffer,"testSome text");
6146 ok (result == 0,
6147 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6148
6149 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6150 ok (result == TRUE,
6151 "EM_STREAMIN with SFF_SELECTION but no selection set "
6152 "should create an undo\n");
6153
6154 /* StreamIn, SFF_SELECTION, with a selection */
6155 es.dwCookie = nCallbackCount;
6156 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6157 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6158 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6159 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6160 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6161 result = strcmp (buffer,"Sometesttext");
6162 ok (result == 0,
6163 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6164
6165 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6166 ok (result == TRUE,
6167 "EM_STREAMIN with SFF_SELECTION and selection set "
6168 "should create an undo\n");
6169
6170 DestroyWindow(hwndRichEdit);
6171 }
6172
is_em_settextex_supported(HWND hwnd)6173 static BOOL is_em_settextex_supported(HWND hwnd)
6174 {
6175 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6176 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6177 }
6178
test_unicode_conversions(void)6179 static void test_unicode_conversions(void)
6180 {
6181 static const WCHAR tW[] = {'t',0};
6182 static const WCHAR teW[] = {'t','e',0};
6183 static const WCHAR textW[] = {'t','e','s','t',0};
6184 static const char textA[] = "test";
6185 char bufA[64];
6186 WCHAR bufW[64];
6187 HWND hwnd;
6188 int em_settextex_supported, ret;
6189
6190 #define set_textA(hwnd, wm_set_text, txt) \
6191 do { \
6192 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6193 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6194 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6195 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6196 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
6197 } while(0)
6198 #define expect_textA(hwnd, wm_get_text, txt) \
6199 do { \
6200 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6201 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
6202 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6203 memset(bufA, 0xAA, sizeof(bufA)); \
6204 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6205 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
6206 ret = lstrcmpA(bufA, txt); \
6207 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6208 } while(0)
6209
6210 #define set_textW(hwnd, wm_set_text, txt) \
6211 do { \
6212 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6213 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6214 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6215 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6216 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
6217 } while(0)
6218 #define expect_textW(hwnd, wm_get_text, txt) \
6219 do { \
6220 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6221 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
6222 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6223 memset(bufW, 0xAA, sizeof(bufW)); \
6224 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6225 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
6226 ret = lstrcmpW(bufW, txt); \
6227 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6228 } while(0)
6229 #define expect_empty(hwnd, wm_get_text) \
6230 do { \
6231 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6232 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
6233 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6234 memset(bufA, 0xAA, sizeof(bufA)); \
6235 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6236 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6237 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6238 } while(0)
6239
6240 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6241 0, 0, 200, 60, 0, 0, 0, 0);
6242 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6243
6244 ret = IsWindowUnicode(hwnd);
6245 ok(ret, "RichEdit20W should be unicode under NT\n");
6246
6247 /* EM_SETTEXTEX is supported starting from version 3.0 */
6248 em_settextex_supported = is_em_settextex_supported(hwnd);
6249 trace("EM_SETTEXTEX is %ssupported on this platform\n",
6250 em_settextex_supported ? "" : "NOT ");
6251
6252 expect_empty(hwnd, WM_GETTEXT);
6253 expect_empty(hwnd, EM_GETTEXTEX);
6254
6255 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6256 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6257 expect_textA(hwnd, WM_GETTEXT, "t");
6258 expect_textA(hwnd, EM_GETTEXTEX, "t");
6259 expect_textW(hwnd, EM_GETTEXTEX, tW);
6260
6261 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6262 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6263 expect_textA(hwnd, WM_GETTEXT, "te");
6264 expect_textA(hwnd, EM_GETTEXTEX, "te");
6265 expect_textW(hwnd, EM_GETTEXTEX, teW);
6266
6267 set_textA(hwnd, WM_SETTEXT, NULL);
6268 expect_empty(hwnd, WM_GETTEXT);
6269 expect_empty(hwnd, EM_GETTEXTEX);
6270
6271 set_textA(hwnd, WM_SETTEXT, textA);
6272 expect_textA(hwnd, WM_GETTEXT, textA);
6273 expect_textA(hwnd, EM_GETTEXTEX, textA);
6274 expect_textW(hwnd, EM_GETTEXTEX, textW);
6275
6276 if (em_settextex_supported)
6277 {
6278 set_textA(hwnd, EM_SETTEXTEX, textA);
6279 expect_textA(hwnd, WM_GETTEXT, textA);
6280 expect_textA(hwnd, EM_GETTEXTEX, textA);
6281 expect_textW(hwnd, EM_GETTEXTEX, textW);
6282 }
6283
6284 set_textW(hwnd, WM_SETTEXT, textW);
6285 expect_textW(hwnd, WM_GETTEXT, textW);
6286 expect_textA(hwnd, WM_GETTEXT, textA);
6287 expect_textW(hwnd, EM_GETTEXTEX, textW);
6288 expect_textA(hwnd, EM_GETTEXTEX, textA);
6289
6290 if (em_settextex_supported)
6291 {
6292 set_textW(hwnd, EM_SETTEXTEX, textW);
6293 expect_textW(hwnd, WM_GETTEXT, textW);
6294 expect_textA(hwnd, WM_GETTEXT, textA);
6295 expect_textW(hwnd, EM_GETTEXTEX, textW);
6296 expect_textA(hwnd, EM_GETTEXTEX, textA);
6297 }
6298 DestroyWindow(hwnd);
6299
6300 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6301 0, 0, 200, 60, 0, 0, 0, 0);
6302 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6303
6304 ret = IsWindowUnicode(hwnd);
6305 ok(!ret, "RichEdit20A should NOT be unicode\n");
6306
6307 set_textA(hwnd, WM_SETTEXT, textA);
6308 expect_textA(hwnd, WM_GETTEXT, textA);
6309 expect_textA(hwnd, EM_GETTEXTEX, textA);
6310 expect_textW(hwnd, EM_GETTEXTEX, textW);
6311
6312 if (em_settextex_supported)
6313 {
6314 set_textA(hwnd, EM_SETTEXTEX, textA);
6315 expect_textA(hwnd, WM_GETTEXT, textA);
6316 expect_textA(hwnd, EM_GETTEXTEX, textA);
6317 expect_textW(hwnd, EM_GETTEXTEX, textW);
6318 }
6319
6320 set_textW(hwnd, WM_SETTEXT, textW);
6321 expect_textW(hwnd, WM_GETTEXT, textW);
6322 expect_textA(hwnd, WM_GETTEXT, textA);
6323 expect_textW(hwnd, EM_GETTEXTEX, textW);
6324 expect_textA(hwnd, EM_GETTEXTEX, textA);
6325
6326 if (em_settextex_supported)
6327 {
6328 set_textW(hwnd, EM_SETTEXTEX, textW);
6329 expect_textW(hwnd, WM_GETTEXT, textW);
6330 expect_textA(hwnd, WM_GETTEXT, textA);
6331 expect_textW(hwnd, EM_GETTEXTEX, textW);
6332 expect_textA(hwnd, EM_GETTEXTEX, textA);
6333 }
6334 DestroyWindow(hwnd);
6335 }
6336
test_WM_CHAR(void)6337 static void test_WM_CHAR(void)
6338 {
6339 HWND hwnd;
6340 int ret;
6341 const char * char_list = "abc\rabc\r";
6342 const char * expected_content_single = "abcabc";
6343 const char * expected_content_multi = "abc\r\nabc\r\n";
6344 char buffer[64] = {0};
6345 const char * p;
6346
6347 /* single-line control must IGNORE carriage returns */
6348 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6349 0, 0, 200, 60, 0, 0, 0, 0);
6350 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6351
6352 p = char_list;
6353 while (*p != '\0') {
6354 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6355 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6356 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6357 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6358 p++;
6359 }
6360
6361 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6362 ret = strcmp(buffer, expected_content_single);
6363 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6364
6365 DestroyWindow(hwnd);
6366
6367 /* multi-line control inserts CR normally */
6368 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6369 0, 0, 200, 60, 0, 0, 0, 0);
6370 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6371
6372 p = char_list;
6373 while (*p != '\0') {
6374 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6375 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6376 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6377 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6378 p++;
6379 }
6380
6381 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6382 ret = strcmp(buffer, expected_content_multi);
6383 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6384
6385 DestroyWindow(hwnd);
6386 }
6387
test_EM_GETTEXTLENGTHEX(void)6388 static void test_EM_GETTEXTLENGTHEX(void)
6389 {
6390 HWND hwnd;
6391 GETTEXTLENGTHEX gtl;
6392 int ret;
6393 const char * base_string = "base string";
6394 const char * test_string = "a\nb\n\n\r\n";
6395 const char * test_string_after = "a";
6396 const char * test_string_2 = "a\rtest\rstring";
6397 char buffer[64] = {0};
6398
6399 /* single line */
6400 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6401 0, 0, 200, 60, 0, 0, 0, 0);
6402 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6403
6404 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6405 gtl.codepage = CP_ACP;
6406 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6407 ok(ret == 0, "ret %d\n",ret);
6408
6409 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6410 gtl.codepage = CP_ACP;
6411 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6412 ok(ret == 0, "ret %d\n",ret);
6413
6414 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6415
6416 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6417 gtl.codepage = CP_ACP;
6418 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6419 ok(ret == strlen(base_string), "ret %d\n",ret);
6420
6421 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6422 gtl.codepage = CP_ACP;
6423 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6424 ok(ret == strlen(base_string), "ret %d\n",ret);
6425
6426 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6427
6428 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6429 gtl.codepage = CP_ACP;
6430 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6431 ok(ret == 1, "ret %d\n",ret);
6432
6433 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6434 gtl.codepage = CP_ACP;
6435 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6436 ok(ret == 1, "ret %d\n",ret);
6437
6438 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6439 ret = strcmp(buffer, test_string_after);
6440 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6441
6442 DestroyWindow(hwnd);
6443
6444 /* multi line */
6445 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6446 0, 0, 200, 60, 0, 0, 0, 0);
6447 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6448
6449 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6450 gtl.codepage = CP_ACP;
6451 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6452 ok(ret == 0, "ret %d\n",ret);
6453
6454 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6455 gtl.codepage = CP_ACP;
6456 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6457 ok(ret == 0, "ret %d\n",ret);
6458
6459 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6460
6461 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6462 gtl.codepage = CP_ACP;
6463 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6464 ok(ret == strlen(base_string), "ret %d\n",ret);
6465
6466 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6467 gtl.codepage = CP_ACP;
6468 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6469 ok(ret == strlen(base_string), "ret %d\n",ret);
6470
6471 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6472
6473 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6474 gtl.codepage = CP_ACP;
6475 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6476 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6477
6478 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6479 gtl.codepage = CP_ACP;
6480 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6481 ok(ret == strlen(test_string_2), "ret %d\n",ret);
6482
6483 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6484
6485 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6486 gtl.codepage = CP_ACP;
6487 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6488 ok(ret == 10, "ret %d\n",ret);
6489
6490 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6491 gtl.codepage = CP_ACP;
6492 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6493 ok(ret == 6, "ret %d\n",ret);
6494
6495 /* Unicode/NUMCHARS/NUMBYTES */
6496 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6497
6498 gtl.flags = GTL_DEFAULT;
6499 gtl.codepage = 1200;
6500 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6501 ok(ret == lstrlenA(test_string_2),
6502 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6503
6504 gtl.flags = GTL_NUMCHARS;
6505 gtl.codepage = 1200;
6506 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6507 ok(ret == lstrlenA(test_string_2),
6508 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6509
6510 gtl.flags = GTL_NUMBYTES;
6511 gtl.codepage = 1200;
6512 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6513 ok(ret == lstrlenA(test_string_2)*2,
6514 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6515
6516 gtl.flags = GTL_PRECISE;
6517 gtl.codepage = 1200;
6518 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6519 ok(ret == lstrlenA(test_string_2)*2,
6520 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6521
6522 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6523 gtl.codepage = 1200;
6524 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6525 ok(ret == lstrlenA(test_string_2),
6526 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6527
6528 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6529 gtl.codepage = 1200;
6530 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
6531 ok(ret == E_INVALIDARG,
6532 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6533
6534 DestroyWindow(hwnd);
6535 }
6536
6537
6538 /* globals that parent and child access when checking event masks & notifications */
6539 static HWND eventMaskEditHwnd = 0;
6540 static int queriedEventMask;
6541 static int watchForEventMask = 0;
6542
6543 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6544 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6545 {
6546 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6547 {
6548 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6549 }
6550 return DefWindowProcA(hwnd, message, wParam, lParam);
6551 }
6552
6553 /* test event masks in combination with WM_COMMAND */
test_eventMask(void)6554 static void test_eventMask(void)
6555 {
6556 HWND parent;
6557 int ret, style;
6558 WNDCLASSA cls;
6559 const char text[] = "foo bar\n";
6560 int eventMask;
6561
6562 /* register class to capture WM_COMMAND */
6563 cls.style = 0;
6564 cls.lpfnWndProc = ParentMsgCheckProcA;
6565 cls.cbClsExtra = 0;
6566 cls.cbWndExtra = 0;
6567 cls.hInstance = GetModuleHandleA(0);
6568 cls.hIcon = 0;
6569 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6570 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6571 cls.lpszMenuName = NULL;
6572 cls.lpszClassName = "EventMaskParentClass";
6573 if(!RegisterClassA(&cls)) assert(0);
6574
6575 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6576 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6577 ok (parent != 0, "Failed to create parent window\n");
6578
6579 eventMaskEditHwnd = new_richedit(parent);
6580 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6581
6582 eventMask = ENM_CHANGE | ENM_UPDATE;
6583 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6584 ok(ret == ENM_NONE, "wrong event mask\n");
6585 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6586 ok(ret == eventMask, "failed to set event mask\n");
6587
6588 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6589 queriedEventMask = 0; /* initialize to something other than we expect */
6590 watchForEventMask = EN_CHANGE;
6591 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6592 ok(ret == TRUE, "failed to set text\n");
6593 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6594 notification in response to WM_SETTEXT */
6595 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6596 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6597
6598 /* check to see if EN_CHANGE is sent when redraw is turned off */
6599 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6600 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6601 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6602 /* redraw is disabled by making the window invisible. */
6603 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6604 queriedEventMask = 0; /* initialize to something other than we expect */
6605 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6606 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6607 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6608 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6609 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6610
6611 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6612 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6613 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6614 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6615 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6616 watchForEventMask = EN_UPDATE;
6617 queriedEventMask = 0; /* initialize to something other than we expect */
6618 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6619 ok(queriedEventMask == 0,
6620 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6621 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6622 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6623 queriedEventMask = 0; /* initialize to something other than we expect */
6624 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6625 ok(queriedEventMask == eventMask,
6626 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6627
6628
6629 DestroyWindow(parent);
6630 }
6631
6632 static int received_WM_NOTIFY = 0;
6633 static int modify_at_WM_NOTIFY = 0;
6634 static BOOL filter_on_WM_NOTIFY = FALSE;
6635 static HWND hwndRichedit_WM_NOTIFY;
6636
WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6637 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6638 {
6639 if(message == WM_NOTIFY)
6640 {
6641 received_WM_NOTIFY = 1;
6642 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6643 if (filter_on_WM_NOTIFY) return TRUE;
6644 }
6645 return DefWindowProcA(hwnd, message, wParam, lParam);
6646 }
6647
test_WM_NOTIFY(void)6648 static void test_WM_NOTIFY(void)
6649 {
6650 HWND parent;
6651 WNDCLASSA cls;
6652 CHARFORMAT2A cf2;
6653 int sel_start, sel_end;
6654
6655 /* register class to capture WM_NOTIFY */
6656 cls.style = 0;
6657 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6658 cls.cbClsExtra = 0;
6659 cls.cbWndExtra = 0;
6660 cls.hInstance = GetModuleHandleA(0);
6661 cls.hIcon = 0;
6662 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6663 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6664 cls.lpszMenuName = NULL;
6665 cls.lpszClassName = "WM_NOTIFY_ParentClass";
6666 if(!RegisterClassA(&cls)) assert(0);
6667
6668 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6669 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6670 ok (parent != 0, "Failed to create parent window\n");
6671
6672 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6673 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6674
6675 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6676
6677 /* Notifications for selection change should only be sent when selection
6678 actually changes. EM_SETCHARFORMAT is one message that calls
6679 ME_CommitUndo, which should check whether message should be sent */
6680 received_WM_NOTIFY = 0;
6681 cf2.cbSize = sizeof(CHARFORMAT2A);
6682 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6683 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6684 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6685 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6686 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6687
6688 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6689 already at 0. */
6690 received_WM_NOTIFY = 0;
6691 modify_at_WM_NOTIFY = 0;
6692 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6693 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6694 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6695
6696 received_WM_NOTIFY = 0;
6697 modify_at_WM_NOTIFY = 0;
6698 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6699 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6700
6701 received_WM_NOTIFY = 0;
6702 modify_at_WM_NOTIFY = 0;
6703 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6704 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6705 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6706
6707 /* Test for WM_NOTIFY messages with redraw disabled. */
6708 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6709 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6710 received_WM_NOTIFY = 0;
6711 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6712 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6713 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6714
6715 /* Test filtering key events. */
6716 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6717 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6718 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6719 received_WM_NOTIFY = 0;
6720 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6721 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6722 ok(sel_start == 1 && sel_end == 1,
6723 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6724 filter_on_WM_NOTIFY = TRUE;
6725 received_WM_NOTIFY = 0;
6726 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6727 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6728 ok(sel_start == 1 && sel_end == 1,
6729 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6730
6731 /* test with owner set to NULL */
6732 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6733 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6734 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6735 ok(sel_start == 1 && sel_end == 1,
6736 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6737
6738 DestroyWindow(hwndRichedit_WM_NOTIFY);
6739 DestroyWindow(parent);
6740 }
6741
6742 static ENLINK enlink;
6743 #define CURSOR_CLIENT_X 5
6744 #define CURSOR_CLIENT_Y 5
6745 #define WP_PARENT 1
6746 #define WP_CHILD 2
6747
EN_LINK_ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6748 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6749 {
6750 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6751 {
6752 enlink = *(ENLINK*)lParam;
6753 }
6754 return DefWindowProcA(hwnd, message, wParam, lParam);
6755 }
6756
link_notify_test(const char * desc,int i,HWND hwnd,HWND parent,UINT msg,WPARAM wParam,LPARAM lParam,BOOL notifies)6757 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6758 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6759 {
6760 ENLINK junk_enlink;
6761
6762 switch (msg)
6763 {
6764 case WM_LBUTTONDBLCLK:
6765 case WM_LBUTTONDOWN:
6766 case WM_LBUTTONUP:
6767 case WM_MOUSEHOVER:
6768 case WM_MOUSEMOVE:
6769 case WM_MOUSEWHEEL:
6770 case WM_RBUTTONDBLCLK:
6771 case WM_RBUTTONDOWN:
6772 case WM_RBUTTONUP:
6773 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6774 break;
6775 case WM_SETCURSOR:
6776 if (wParam == WP_PARENT)
6777 wParam = (WPARAM)parent;
6778 else if (wParam == WP_CHILD)
6779 wParam = (WPARAM)hwnd;
6780 break;
6781 }
6782
6783 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6784 enlink = junk_enlink;
6785
6786 SendMessageA(hwnd, msg, wParam, lParam);
6787
6788 if (notifies)
6789 {
6790 ok(enlink.nmhdr.hwndFrom == hwnd,
6791 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6792 ok(enlink.nmhdr.idFrom == 0,
6793 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6794 ok(enlink.msg == msg,
6795 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6796 if (msg == WM_SETCURSOR)
6797 {
6798 ok(enlink.wParam == 0,
6799 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6800 }
6801 else
6802 {
6803 ok(enlink.wParam == wParam,
6804 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6805 }
6806 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6807 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6808 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6809 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6810 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6811 }
6812 else
6813 {
6814 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6815 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6816 }
6817 }
6818
test_EN_LINK(void)6819 static void test_EN_LINK(void)
6820 {
6821 HWND hwnd, parent;
6822 WNDCLASSA cls;
6823 CHARFORMAT2A cf2;
6824 POINT orig_cursor_pos;
6825 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6826 int i;
6827
6828 static const struct
6829 {
6830 UINT msg;
6831 WPARAM wParam;
6832 LPARAM lParam;
6833 BOOL notifies;
6834 }
6835 link_notify_tests[] =
6836 {
6837 /* hold down the left button and try some messages */
6838 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6839 { EM_LINESCROLL, 0, 1, FALSE },
6840 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6841 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6842 { WM_MOUSEHOVER, 0, 0, FALSE },
6843 { WM_MOUSEMOVE, 0, 0, FALSE },
6844 { WM_MOUSEWHEEL, 0, 0, FALSE },
6845 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6846 { WM_RBUTTONDOWN, 0, 0, TRUE },
6847 { WM_RBUTTONUP, 0, 0, TRUE },
6848 { WM_SETCURSOR, 0, 0, FALSE },
6849 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6850 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6851 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6852 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6853 { WM_LBUTTONUP, 0, 0, TRUE },
6854 /* hold down the right button and try some messages */
6855 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6856 { EM_LINESCROLL, 0, 1, FALSE },
6857 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6858 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6859 { WM_LBUTTONDOWN, 0, 0, TRUE },
6860 { WM_LBUTTONUP, 0, 0, TRUE },
6861 { WM_MOUSEHOVER, 0, 0, FALSE },
6862 { WM_MOUSEMOVE, 0, 0, TRUE },
6863 { WM_MOUSEWHEEL, 0, 0, FALSE },
6864 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6865 { WM_SETCURSOR, 0, 0, FALSE },
6866 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6867 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6868 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6869 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6870 { WM_RBUTTONUP, 0, 0, TRUE },
6871 /* try the messages with both buttons released */
6872 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6873 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6874 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6875 { WM_LBUTTONDOWN, 0, 0, TRUE },
6876 { WM_LBUTTONUP, 0, 0, TRUE },
6877 { WM_MOUSEHOVER, 0, 0, FALSE },
6878 { WM_MOUSEMOVE, 0, 0, TRUE },
6879 { WM_MOUSEWHEEL, 0, 0, FALSE },
6880 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6881 { WM_RBUTTONDOWN, 0, 0, TRUE },
6882 { WM_RBUTTONUP, 0, 0, TRUE },
6883 { WM_SETCURSOR, 0, 0, FALSE },
6884 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6885 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6886 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6887 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6888 };
6889
6890 /* register class to capture WM_NOTIFY */
6891 cls.style = 0;
6892 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6893 cls.cbClsExtra = 0;
6894 cls.cbWndExtra = 0;
6895 cls.hInstance = GetModuleHandleA(0);
6896 cls.hIcon = 0;
6897 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6898 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6899 cls.lpszMenuName = NULL;
6900 cls.lpszClassName = "EN_LINK_ParentClass";
6901 if(!RegisterClassA(&cls)) assert(0);
6902
6903 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6904 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6905 ok(parent != 0, "Failed to create parent window\n");
6906
6907 hwnd = new_richedit(parent);
6908 ok(hwnd != 0, "Failed to create edit window\n");
6909
6910 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6911
6912 cf2.cbSize = sizeof(CHARFORMAT2A);
6913 cf2.dwMask = CFM_LINK;
6914 cf2.dwEffects = CFE_LINK;
6915 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6916 /* mixing letters and numbers causes runs to be split */
6917 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6918
6919 GetCursorPos(&orig_cursor_pos);
6920 SetCursorPos(0, 0);
6921
6922 for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
6923 {
6924 link_notify_test("cursor position simulated", i, hwnd, parent,
6925 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6926 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6927 }
6928
6929 ClientToScreen(hwnd, &cursor_screen_pos);
6930 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6931
6932 for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
6933 {
6934 link_notify_test("cursor position set", i, hwnd, parent,
6935 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6936 link_notify_tests[i].notifies);
6937 }
6938
6939 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6940 DestroyWindow(hwnd);
6941 DestroyWindow(parent);
6942 }
6943
test_undo_coalescing(void)6944 static void test_undo_coalescing(void)
6945 {
6946 HWND hwnd;
6947 int result;
6948 char buffer[64] = {0};
6949
6950 /* multi-line control inserts CR normally */
6951 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6952 0, 0, 200, 60, 0, 0, 0, 0);
6953 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6954
6955 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6956 ok (result == FALSE, "Can undo after window creation.\n");
6957 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6958 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6959 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6960 ok (result == FALSE, "Can redo after window creation.\n");
6961 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6962 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6963
6964 /* Test the effect of arrows keys during typing on undo transactions*/
6965 simulate_typing_characters(hwnd, "one two three");
6966 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6967 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6968 simulate_typing_characters(hwnd, " four five six");
6969
6970 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6971 ok (result == FALSE, "Can redo before anything is undone.\n");
6972 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6973 ok (result == TRUE, "Cannot undo typed characters.\n");
6974 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6975 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6976 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6977 ok (result == TRUE, "Cannot redo after undo.\n");
6978 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6979 result = strcmp(buffer, "one two three");
6980 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6981
6982 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6983 ok (result == TRUE, "Cannot undo typed characters.\n");
6984 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6985 ok (result == TRUE, "Failed to undo typed characters.\n");
6986 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6987 result = strcmp(buffer, "");
6988 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6989
6990 /* Test the effect of focus changes during typing on undo transactions*/
6991 simulate_typing_characters(hwnd, "one two three");
6992 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6993 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6994 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6995 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6996 simulate_typing_characters(hwnd, " four five six");
6997 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6998 ok (result == TRUE, "Failed to undo typed characters.\n");
6999 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7000 result = strcmp(buffer, "one two three");
7001 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7002
7003 /* Test the effect of the back key during typing on undo transactions */
7004 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7005 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7006 ok (result == TRUE, "Failed to clear the text.\n");
7007 simulate_typing_characters(hwnd, "one two threa");
7008 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7009 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
7010 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
7011 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
7012 simulate_typing_characters(hwnd, "e four five six");
7013 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7014 ok (result == TRUE, "Failed to undo typed characters.\n");
7015 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7016 result = strcmp(buffer, "");
7017 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7018
7019 /* Test the effect of the delete key during typing on undo transactions */
7020 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7021 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
7022 ok(result == TRUE, "Failed to set the text.\n");
7023 SendMessageA(hwnd, EM_SETSEL, 1, 1);
7024 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7025 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7026 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7027 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7028 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7029 ok (result == TRUE, "Failed to undo typed characters.\n");
7030 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7031 result = strcmp(buffer, "acd");
7032 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
7033 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7034 ok (result == TRUE, "Failed to undo typed characters.\n");
7035 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7036 result = strcmp(buffer, "abcd");
7037 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
7038
7039 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
7040 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7041 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7042 ok (result == TRUE, "Failed to clear the text.\n");
7043 simulate_typing_characters(hwnd, "one two three");
7044 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
7045 ok (result == 0, "expected %d but got %d\n", 0, result);
7046 simulate_typing_characters(hwnd, " four five six");
7047 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7048 ok (result == TRUE, "Failed to undo typed characters.\n");
7049 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7050 result = strcmp(buffer, "one two three");
7051 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7052 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7053 ok (result == TRUE, "Failed to undo typed characters.\n");
7054 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7055 result = strcmp(buffer, "");
7056 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7057
7058 DestroyWindow(hwnd);
7059 }
7060
customWordBreakProc(WCHAR * text,int pos,int bytes,int code)7061 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
7062 {
7063 int length;
7064
7065 /* MSDN lied, length is actually the number of bytes. */
7066 length = bytes / sizeof(WCHAR);
7067 switch(code)
7068 {
7069 case WB_ISDELIMITER:
7070 return text[pos] == 'X';
7071 case WB_LEFT:
7072 case WB_MOVEWORDLEFT:
7073 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7074 return pos-1;
7075 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
7076 case WB_LEFTBREAK:
7077 pos--;
7078 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7079 pos--;
7080 return pos;
7081 case WB_RIGHT:
7082 case WB_MOVEWORDRIGHT:
7083 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7084 return pos+1;
7085 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
7086 case WB_RIGHTBREAK:
7087 pos++;
7088 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7089 pos++;
7090 return pos;
7091 default:
7092 ok(FALSE, "Unexpected code %d\n", code);
7093 break;
7094 }
7095 return 0;
7096 }
7097
test_word_movement(void)7098 static void test_word_movement(void)
7099 {
7100 HWND hwnd;
7101 int result;
7102 int sel_start, sel_end;
7103 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
7104
7105 /* multi-line control inserts CR normally */
7106 hwnd = new_richedit(NULL);
7107
7108 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
7109 ok (result == TRUE, "Failed to clear the text.\n");
7110 SendMessageA(hwnd, EM_SETSEL, 0, 0);
7111 /* |one two three */
7112
7113 send_ctrl_key(hwnd, VK_RIGHT);
7114 /* one |two three */
7115 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7116 ok(sel_start == sel_end, "Selection should be empty\n");
7117 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7118
7119 send_ctrl_key(hwnd, VK_RIGHT);
7120 /* one two |three */
7121 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7122 ok(sel_start == sel_end, "Selection should be empty\n");
7123 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7124
7125 send_ctrl_key(hwnd, VK_LEFT);
7126 /* one |two three */
7127 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7128 ok(sel_start == sel_end, "Selection should be empty\n");
7129 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7130
7131 send_ctrl_key(hwnd, VK_LEFT);
7132 /* |one two three */
7133 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7134 ok(sel_start == sel_end, "Selection should be empty\n");
7135 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
7136
7137 SendMessageA(hwnd, EM_SETSEL, 8, 8);
7138 /* one two | three */
7139 send_ctrl_key(hwnd, VK_RIGHT);
7140 /* one two |three */
7141 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7142 ok(sel_start == sel_end, "Selection should be empty\n");
7143 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7144
7145 SendMessageA(hwnd, EM_SETSEL, 11, 11);
7146 /* one two th|ree */
7147 send_ctrl_key(hwnd, VK_LEFT);
7148 /* one two |three */
7149 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7150 ok(sel_start == sel_end, "Selection should be empty\n");
7151 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7152
7153 /* Test with a custom word break procedure that uses X as the delimiter. */
7154 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
7155 ok (result == TRUE, "Failed to clear the text.\n");
7156 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7157 /* |one twoXthree */
7158 send_ctrl_key(hwnd, VK_RIGHT);
7159 /* one twoX|three */
7160 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7161 ok(sel_start == sel_end, "Selection should be empty\n");
7162 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7163
7164 DestroyWindow(hwnd);
7165
7166 /* Make sure the behaviour is the same with a unicode richedit window,
7167 * and using unicode functions. */
7168
7169 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7170 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7171 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7172
7173 /* Test with a custom word break procedure that uses X as the delimiter. */
7174 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7175 ok (result == TRUE, "Failed to clear the text.\n");
7176 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7177 /* |one twoXthree */
7178 send_ctrl_key(hwnd, VK_RIGHT);
7179 /* one twoX|three */
7180 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7181 ok(sel_start == sel_end, "Selection should be empty\n");
7182 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7183
7184 DestroyWindow(hwnd);
7185 }
7186
test_EM_CHARFROMPOS(void)7187 static void test_EM_CHARFROMPOS(void)
7188 {
7189 HWND hwnd;
7190 int result;
7191 RECT rcClient;
7192 POINTL point;
7193 point.x = 0;
7194 point.y = 40;
7195
7196 /* multi-line control inserts CR normally */
7197 hwnd = new_richedit(NULL);
7198 result = SendMessageA(hwnd, WM_SETTEXT, 0,
7199 (LPARAM)"one two three four five six seven\reight");
7200 ok(result == 1, "Expected 1, got %d\n", result);
7201 GetClientRect(hwnd, &rcClient);
7202
7203 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7204 ok(result == 34, "expected character index of 34 but got %d\n", result);
7205
7206 /* Test with points outside the bounds of the richedit control. */
7207 point.x = -1;
7208 point.y = 40;
7209 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7210 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7211
7212 point.x = 1000;
7213 point.y = 0;
7214 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7215 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
7216
7217 point.x = 1000;
7218 point.y = 36;
7219 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7220 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7221
7222 point.x = 1000;
7223 point.y = -1;
7224 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7225 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7226
7227 point.x = 1000;
7228 point.y = rcClient.bottom + 1;
7229 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7230 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7231
7232 point.x = 1000;
7233 point.y = rcClient.bottom;
7234 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7235 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7236
7237 DestroyWindow(hwnd);
7238 }
7239
test_word_wrap(void)7240 static void test_word_wrap(void)
7241 {
7242 HWND hwnd;
7243 POINTL point = {0, 60}; /* This point must be below the first line */
7244 const char *text = "Must be long enough to test line wrapping";
7245 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7246 int res, pos, lines;
7247
7248 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7249 * when specified on window creation and set later. */
7250 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7251 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7252 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7253 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7254 ok(res, "WM_SETTEXT failed.\n");
7255 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7256 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7257 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7258 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7259
7260 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
7261 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7262 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7263 DestroyWindow(hwnd);
7264
7265 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7266 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7267 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7268
7269 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7270 ok(res, "WM_SETTEXT failed.\n");
7271 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7272 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7273 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7274 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7275
7276 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7277 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7278 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7279 DestroyWindow(hwnd);
7280
7281 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7282 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7283 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7284 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7285 ok(res, "WM_SETTEXT failed.\n");
7286 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7287 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7288
7289 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7290 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7291 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7292 DestroyWindow(hwnd);
7293
7294 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7295 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7296 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7297 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7298 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7299 ok(res, "WM_SETTEXT failed.\n");
7300 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7301 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7302
7303 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7304 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7305 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7306
7307 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7308 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7309 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7310 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7311 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7312
7313 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7314 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7315 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7316 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7317 DestroyWindow(hwnd);
7318
7319 /* Test to see if wrapping happens with redraw disabled. */
7320 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7321 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7322 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7323 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7324 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7325 ok(res, "EM_REPLACESEL failed.\n");
7326 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7327 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7328 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7329 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7330 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7331
7332 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7333 DestroyWindow(hwnd);
7334 }
7335
test_autoscroll(void)7336 static void test_autoscroll(void)
7337 {
7338 HWND hwnd = new_richedit(NULL);
7339 int lines, ret, redraw;
7340 POINT pt;
7341
7342 for (redraw = 0; redraw <= 1; redraw++) {
7343 trace("testing with WM_SETREDRAW=%d\n", redraw);
7344 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7345 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7346 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7347 ok(lines == 8, "%d lines instead of 8\n", lines);
7348 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7349 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7350 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7351 ret = GetWindowLongA(hwnd, GWL_STYLE);
7352 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7353
7354 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7355 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7356 ok(lines == 1, "%d lines instead of 1\n", lines);
7357 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7358 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7359 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7360 ret = GetWindowLongA(hwnd, GWL_STYLE);
7361 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7362 }
7363
7364 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7365 DestroyWindow(hwnd);
7366
7367 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7368 * auto vertical/horizontal scrolling options. */
7369 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7370 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7371 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7372 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7373 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7374 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7375 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7376 ret = GetWindowLongA(hwnd, GWL_STYLE);
7377 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7378 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7379 DestroyWindow(hwnd);
7380
7381 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7382 WS_POPUP|ES_MULTILINE,
7383 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7384 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7385 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7386 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7387 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7388 ret = GetWindowLongA(hwnd, GWL_STYLE);
7389 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7390 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7391 DestroyWindow(hwnd);
7392 }
7393
7394
test_format_rect(void)7395 static void test_format_rect(void)
7396 {
7397 HWND hwnd;
7398 RECT rc, expected, clientRect;
7399 int n;
7400 DWORD options;
7401
7402 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7403 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7404 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7405 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7406
7407 GetClientRect(hwnd, &clientRect);
7408
7409 expected = clientRect;
7410 InflateRect(&expected, -1, 0);
7411 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7412 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7413 wine_dbgstr_rect(&expected));
7414
7415 for (n = -3; n <= 3; n++)
7416 {
7417 rc = clientRect;
7418 InflateRect(&rc, -n, -n);
7419 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7420
7421 expected = rc;
7422 expected.top = max(0, rc.top);
7423 expected.left = max(0, rc.left);
7424 expected.bottom = min(clientRect.bottom, rc.bottom);
7425 expected.right = min(clientRect.right, rc.right);
7426 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7427 ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7428 wine_dbgstr_rect(&expected));
7429 }
7430
7431 rc = clientRect;
7432 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7433 expected = clientRect;
7434 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7435 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7436 wine_dbgstr_rect(&expected));
7437
7438 /* Adding the selectionbar adds the selectionbar width to the left side. */
7439 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7440 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7441 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7442 expected.left += 8; /* selection bar width */
7443 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7444 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7445 wine_dbgstr_rect(&expected));
7446
7447 rc = clientRect;
7448 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7449 expected = clientRect;
7450 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7451 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7452 wine_dbgstr_rect(&expected));
7453
7454 /* Removing the selectionbar subtracts the selectionbar width from the left side,
7455 * even if the left side is already 0. */
7456 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7457 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7458 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7459 expected.left -= 8; /* selection bar width */
7460 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7461 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7462 wine_dbgstr_rect(&expected));
7463
7464 /* Set the absolute value of the formatting rectangle. */
7465 rc = clientRect;
7466 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7467 expected = clientRect;
7468 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7469 ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7470 wine_dbgstr_rect(&expected));
7471
7472 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7473 * LPARAM as being a relative offset when the WPARAM value is 1, but these
7474 * tests show that this isn't true. */
7475 rc.top = 15;
7476 rc.left = 15;
7477 rc.bottom = clientRect.bottom - 15;
7478 rc.right = clientRect.right - 15;
7479 expected = rc;
7480 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7481 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7482 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7483 wine_dbgstr_rect(&expected));
7484
7485 /* For some reason it does not limit the values to the client rect with
7486 * a WPARAM value of 1. */
7487 rc.top = -15;
7488 rc.left = -15;
7489 rc.bottom = clientRect.bottom + 15;
7490 rc.right = clientRect.right + 15;
7491 expected = rc;
7492 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7493 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7494 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7495 wine_dbgstr_rect(&expected));
7496
7497 /* Reset to default rect and check how the format rect adjusts to window
7498 * resize and how it copes with very small windows */
7499 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7500
7501 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7502 GetClientRect(hwnd, &clientRect);
7503
7504 expected = clientRect;
7505 InflateRect(&expected, -1, 0);
7506 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7507 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7508 wine_dbgstr_rect(&expected));
7509
7510 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7511 GetClientRect(hwnd, &clientRect);
7512
7513 expected = clientRect;
7514 InflateRect(&expected, -1, 0);
7515 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7516 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7517 wine_dbgstr_rect(&expected));
7518
7519 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7520 GetClientRect(hwnd, &clientRect);
7521
7522 expected = clientRect;
7523 InflateRect(&expected, -1, 0);
7524 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7525 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7526 wine_dbgstr_rect(&expected));
7527
7528 DestroyWindow(hwnd);
7529
7530 /* The extended window style affects the formatting rectangle. */
7531 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7532 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7533 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7534 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7535
7536 GetClientRect(hwnd, &clientRect);
7537
7538 expected = clientRect;
7539 expected.top += 1;
7540 InflateRect(&expected, -1, 0);
7541 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7542 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7543 wine_dbgstr_rect(&expected));
7544
7545 rc = clientRect;
7546 InflateRect(&rc, -5, -5);
7547 expected = rc;
7548 expected.top -= 1;
7549 InflateRect(&expected, 1, 0);
7550 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7551 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7552 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7553 wine_dbgstr_rect(&expected));
7554
7555 DestroyWindow(hwnd);
7556 }
7557
test_WM_GETDLGCODE(void)7558 static void test_WM_GETDLGCODE(void)
7559 {
7560 HWND hwnd;
7561 UINT res, expected;
7562 MSG msg;
7563
7564 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7565
7566 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7567 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7568 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7569 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7570 msg.hwnd = hwnd;
7571 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7572 expected = expected | DLGC_WANTMESSAGE;
7573 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7574 res, expected);
7575 DestroyWindow(hwnd);
7576
7577 msg.message = WM_KEYDOWN;
7578 msg.wParam = VK_RETURN;
7579 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7580 msg.pt.x = 0;
7581 msg.pt.y = 0;
7582 msg.time = GetTickCount();
7583
7584 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7585 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7586 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7587 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7588 msg.hwnd = hwnd;
7589 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7590 expected = expected | DLGC_WANTMESSAGE;
7591 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7592 res, expected);
7593 DestroyWindow(hwnd);
7594
7595 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7596 ES_MULTILINE|WS_POPUP,
7597 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7598 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7599 msg.hwnd = hwnd;
7600 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7601 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7602 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7603 res, expected);
7604 DestroyWindow(hwnd);
7605
7606 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7607 ES_WANTRETURN|WS_POPUP,
7608 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7609 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7610 msg.hwnd = hwnd;
7611 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7612 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7613 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7614 res, expected);
7615 DestroyWindow(hwnd);
7616
7617 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7618 WS_POPUP,
7619 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7620 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7621 msg.hwnd = hwnd;
7622 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7623 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7624 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7625 res, expected);
7626 DestroyWindow(hwnd);
7627
7628 msg.wParam = VK_TAB;
7629 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7630
7631 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7632 ES_MULTILINE|WS_POPUP,
7633 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7634 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7635 msg.hwnd = hwnd;
7636 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7637 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7638 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7639 res, expected);
7640 DestroyWindow(hwnd);
7641
7642 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7643 WS_POPUP,
7644 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7645 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7646 msg.hwnd = hwnd;
7647 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7648 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7649 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7650 res, expected);
7651 DestroyWindow(hwnd);
7652
7653 hold_key(VK_CONTROL);
7654
7655 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7656 ES_MULTILINE|WS_POPUP,
7657 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7658 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7659 msg.hwnd = hwnd;
7660 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7661 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7662 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7663 res, expected);
7664 DestroyWindow(hwnd);
7665
7666 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7667 WS_POPUP,
7668 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7669 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7670 msg.hwnd = hwnd;
7671 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7672 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7673 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7674 res, expected);
7675 DestroyWindow(hwnd);
7676
7677 release_key(VK_CONTROL);
7678
7679 msg.wParam = 'a';
7680 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7681
7682 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7683 ES_MULTILINE|WS_POPUP,
7684 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7685 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7686 msg.hwnd = hwnd;
7687 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7688 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7689 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7690 res, expected);
7691 DestroyWindow(hwnd);
7692
7693 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7694 WS_POPUP,
7695 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7696 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7697 msg.hwnd = hwnd;
7698 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7699 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7700 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7701 res, expected);
7702 DestroyWindow(hwnd);
7703
7704 msg.message = WM_CHAR;
7705
7706 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7707 ES_MULTILINE|WS_POPUP,
7708 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7709 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7710 msg.hwnd = hwnd;
7711 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7712 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7713 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7714 res, expected);
7715 DestroyWindow(hwnd);
7716
7717 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7718 WS_POPUP,
7719 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7720 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7721 msg.hwnd = hwnd;
7722 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7723 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7724 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7725 res, expected);
7726 DestroyWindow(hwnd);
7727
7728 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7729 WS_POPUP|ES_SAVESEL,
7730 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7731 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7732 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7733 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7734 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7735 res, expected);
7736 DestroyWindow(hwnd);
7737 }
7738
test_zoom(void)7739 static void test_zoom(void)
7740 {
7741 HWND hwnd;
7742 UINT ret;
7743 RECT rc;
7744 POINT pt;
7745 int numerator, denominator;
7746
7747 hwnd = new_richedit(NULL);
7748 GetClientRect(hwnd, &rc);
7749 pt.x = (rc.right - rc.left) / 2;
7750 pt.y = (rc.bottom - rc.top) / 2;
7751 ClientToScreen(hwnd, &pt);
7752
7753 /* Test initial zoom value */
7754 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7755 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7756 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7757 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7758
7759 /* test scroll wheel */
7760 hold_key(VK_CONTROL);
7761 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7762 MAKELPARAM(pt.x, pt.y));
7763 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7764 release_key(VK_CONTROL);
7765
7766 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7767 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7768 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7769 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7770
7771 /* Test how much the mouse wheel can zoom in and out. */
7772 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7773 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7774
7775 hold_key(VK_CONTROL);
7776 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7777 MAKELPARAM(pt.x, pt.y));
7778 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7779 release_key(VK_CONTROL);
7780
7781 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7782 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7783 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7784 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7785
7786 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7787 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7788
7789 hold_key(VK_CONTROL);
7790 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7791 MAKELPARAM(pt.x, pt.y));
7792 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7793 release_key(VK_CONTROL);
7794
7795 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7796 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7797 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7798 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7799
7800 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7801 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7802
7803 hold_key(VK_CONTROL);
7804 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7805 MAKELPARAM(pt.x, pt.y));
7806 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7807 release_key(VK_CONTROL);
7808
7809 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7810 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7811 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7812 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7813
7814 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7815 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7816
7817 hold_key(VK_CONTROL);
7818 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7819 MAKELPARAM(pt.x, pt.y));
7820 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7821 release_key(VK_CONTROL);
7822
7823 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7824 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7825 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7826 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7827
7828 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7829 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7830 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7831
7832 hold_key(VK_CONTROL);
7833 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7834 MAKELPARAM(pt.x, pt.y));
7835 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7836 release_key(VK_CONTROL);
7837
7838 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7839 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7840 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7841 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7842
7843 /* Test bounds checking on EM_SETZOOM */
7844 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7845 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7846
7847 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7848 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7849
7850 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7851 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7852
7853 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7854 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7855 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7856 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7857
7858 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7859 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7860
7861 /* See if negative numbers are accepted. */
7862 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7863 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7864
7865 /* See if negative numbers are accepted. */
7866 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7867 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7868
7869 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7870 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7871 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7872 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7873
7874 /* Reset the zoom value */
7875 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7876 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7877
7878 DestroyWindow(hwnd);
7879 }
7880
7881 struct dialog_mode_messages
7882 {
7883 int wm_getdefid, wm_close, wm_nextdlgctl;
7884 };
7885
7886 static struct dialog_mode_messages dm_messages;
7887
7888 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7889 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7890 "got %d\n", wmclose, dm_messages.wm_close); \
7891 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7892 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7893 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7894 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7895
dialog_mode_wnd_proc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)7896 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7897 {
7898 switch (iMsg)
7899 {
7900 case DM_GETDEFID:
7901 dm_messages.wm_getdefid++;
7902 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7903 case WM_NEXTDLGCTL:
7904 dm_messages.wm_nextdlgctl++;
7905 break;
7906 case WM_CLOSE:
7907 dm_messages.wm_close++;
7908 break;
7909 }
7910
7911 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7912 }
7913
test_dialogmode(void)7914 static void test_dialogmode(void)
7915 {
7916 HWND hwRichEdit, hwParent, hwButton;
7917 MSG msg= {0};
7918 int lcount, r;
7919 WNDCLASSA cls;
7920
7921 cls.style = 0;
7922 cls.lpfnWndProc = dialog_mode_wnd_proc;
7923 cls.cbClsExtra = 0;
7924 cls.cbWndExtra = 0;
7925 cls.hInstance = GetModuleHandleA(0);
7926 cls.hIcon = 0;
7927 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7928 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7929 cls.lpszMenuName = NULL;
7930 cls.lpszClassName = "DialogModeParentClass";
7931 if(!RegisterClassA(&cls)) assert(0);
7932
7933 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7934 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7935
7936 /* Test richedit(ES_MULTILINE) */
7937
7938 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7939
7940 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7941 ok(0 == r, "expected 0, got %d\n", r);
7942 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7943 ok(2 == lcount, "expected 2, got %d\n", lcount);
7944
7945 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7946 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7947
7948 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7949 ok(0 == r, "expected 0, got %d\n", r);
7950 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7951 ok(3 == lcount, "expected 3, got %d\n", lcount);
7952
7953 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7954 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7955 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7956 ok(0 == r, "expected 0, got %d\n", r);
7957 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7958 ok(3 == lcount, "expected 3, got %d\n", lcount);
7959
7960 DestroyWindow(hwRichEdit);
7961
7962 /* Test standalone richedit(ES_MULTILINE) */
7963
7964 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7965
7966 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7967 ok(0 == r, "expected 0, got %d\n", r);
7968 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7969 ok(2 == lcount, "expected 2, got %d\n", lcount);
7970
7971 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7972 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7973
7974 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7975 ok(0 == r, "expected 0, got %d\n", r);
7976 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7977 ok(2 == lcount, "expected 2, got %d\n", lcount);
7978
7979 DestroyWindow(hwRichEdit);
7980
7981 /* Check a destination for messages */
7982
7983 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7984
7985 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7986 SetParent( hwRichEdit, NULL);
7987
7988 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7989 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7990
7991 memset(&dm_messages, 0, sizeof(dm_messages));
7992 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7993 ok(0 == r, "expected 0, got %d\n", r);
7994 test_dm_messages(0, 1, 0);
7995
7996 memset(&dm_messages, 0, sizeof(dm_messages));
7997 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7998 ok(0 == r, "expected 0, got %d\n", r);
7999 test_dm_messages(0, 0, 1);
8000
8001 DestroyWindow(hwRichEdit);
8002
8003 /* Check messages from richedit(ES_MULTILINE) */
8004
8005 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
8006
8007 memset(&dm_messages, 0, sizeof(dm_messages));
8008 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8009 ok(0 == r, "expected 0, got %d\n", r);
8010 test_dm_messages(0, 0, 0);
8011
8012 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8013 ok(2 == lcount, "expected 2, got %d\n", lcount);
8014
8015 memset(&dm_messages, 0, sizeof(dm_messages));
8016 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8017 ok(0 == r, "expected 0, got %d\n", r);
8018 test_dm_messages(0, 0, 0);
8019
8020 memset(&dm_messages, 0, sizeof(dm_messages));
8021 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8022 ok(0 == r, "expected 0, got %d\n", r);
8023 test_dm_messages(0, 0, 0);
8024
8025 memset(&dm_messages, 0, sizeof(dm_messages));
8026 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8027 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8028 test_dm_messages(0, 0, 0);
8029
8030 memset(&dm_messages, 0, sizeof(dm_messages));
8031 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8032 ok(0 == r, "expected 0, got %d\n", r);
8033 test_dm_messages(0, 1, 0);
8034
8035 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8036 ok(2 == lcount, "expected 2, got %d\n", lcount);
8037
8038 memset(&dm_messages, 0, sizeof(dm_messages));
8039 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8040 ok(0 == r, "expected 0, got %d\n", r);
8041 test_dm_messages(0, 0, 0);
8042
8043 memset(&dm_messages, 0, sizeof(dm_messages));
8044 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8045 ok(0 == r, "expected 0, got %d\n", r);
8046 test_dm_messages(0, 0, 1);
8047
8048 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8049 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8050 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8051
8052 memset(&dm_messages, 0, sizeof(dm_messages));
8053 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8054 ok(0 == r, "expected 0, got %d\n", r);
8055 test_dm_messages(0, 1, 1);
8056
8057 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8058 ok(2 == lcount, "expected 2, got %d\n", lcount);
8059
8060 DestroyWindow(hwButton);
8061 DestroyWindow(hwRichEdit);
8062
8063 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
8064
8065 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
8066
8067 memset(&dm_messages, 0, sizeof(dm_messages));
8068 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8069 ok(0 == r, "expected 0, got %d\n", r);
8070 test_dm_messages(0, 0, 0);
8071
8072 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8073 ok(2 == lcount, "expected 2, got %d\n", lcount);
8074
8075 memset(&dm_messages, 0, sizeof(dm_messages));
8076 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8077 ok(0 == r, "expected 0, got %d\n", r);
8078 test_dm_messages(0, 0, 0);
8079
8080 memset(&dm_messages, 0, sizeof(dm_messages));
8081 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8082 ok(0 == r, "expected 0, got %d\n", r);
8083 test_dm_messages(0, 0, 0);
8084
8085 memset(&dm_messages, 0, sizeof(dm_messages));
8086 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8087 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8088 test_dm_messages(0, 0, 0);
8089
8090 memset(&dm_messages, 0, sizeof(dm_messages));
8091 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8092 ok(0 == r, "expected 0, got %d\n", r);
8093 test_dm_messages(0, 0, 0);
8094
8095 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8096 ok(3 == lcount, "expected 3, got %d\n", lcount);
8097
8098 memset(&dm_messages, 0, sizeof(dm_messages));
8099 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8100 ok(0 == r, "expected 0, got %d\n", r);
8101 test_dm_messages(0, 0, 0);
8102
8103 memset(&dm_messages, 0, sizeof(dm_messages));
8104 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8105 ok(0 == r, "expected 0, got %d\n", r);
8106 test_dm_messages(0, 0, 1);
8107
8108 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8109 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8110 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8111
8112 memset(&dm_messages, 0, sizeof(dm_messages));
8113 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8114 ok(0 == r, "expected 0, got %d\n", r);
8115 test_dm_messages(0, 0, 0);
8116
8117 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8118 ok(4 == lcount, "expected 4, got %d\n", lcount);
8119
8120 DestroyWindow(hwButton);
8121 DestroyWindow(hwRichEdit);
8122
8123 /* Check messages from richedit(0) */
8124
8125 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
8126
8127 memset(&dm_messages, 0, sizeof(dm_messages));
8128 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8129 ok(0 == r, "expected 0, got %d\n", r);
8130 test_dm_messages(0, 0, 0);
8131
8132 memset(&dm_messages, 0, sizeof(dm_messages));
8133 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8134 ok(0 == r, "expected 0, got %d\n", r);
8135 test_dm_messages(0, 0, 0);
8136
8137 memset(&dm_messages, 0, sizeof(dm_messages));
8138 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8139 ok(0 == r, "expected 0, got %d\n", r);
8140 test_dm_messages(0, 0, 0);
8141
8142 memset(&dm_messages, 0, sizeof(dm_messages));
8143 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8144 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8145 test_dm_messages(0, 0, 0);
8146
8147 memset(&dm_messages, 0, sizeof(dm_messages));
8148 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8149 ok(0 == r, "expected 0, got %d\n", r);
8150 test_dm_messages(0, 1, 0);
8151
8152 memset(&dm_messages, 0, sizeof(dm_messages));
8153 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8154 ok(0 == r, "expected 0, got %d\n", r);
8155 test_dm_messages(0, 0, 0);
8156
8157 memset(&dm_messages, 0, sizeof(dm_messages));
8158 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8159 ok(0 == r, "expected 0, got %d\n", r);
8160 test_dm_messages(0, 0, 1);
8161
8162 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8163 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8164 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8165
8166 memset(&dm_messages, 0, sizeof(dm_messages));
8167 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8168 ok(0 == r, "expected 0, got %d\n", r);
8169 test_dm_messages(0, 1, 1);
8170
8171 DestroyWindow(hwRichEdit);
8172
8173 /* Check messages from richedit(ES_WANTRETURN) */
8174
8175 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8176
8177 memset(&dm_messages, 0, sizeof(dm_messages));
8178 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8179 ok(0 == r, "expected 0, got %d\n", r);
8180 test_dm_messages(0, 0, 0);
8181
8182 memset(&dm_messages, 0, sizeof(dm_messages));
8183 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8184 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8185 test_dm_messages(0, 0, 0);
8186
8187 memset(&dm_messages, 0, sizeof(dm_messages));
8188 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8189 ok(0 == r, "expected 0, got %d\n", r);
8190 test_dm_messages(0, 0, 0);
8191
8192 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8193 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8194 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8195
8196 memset(&dm_messages, 0, sizeof(dm_messages));
8197 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8198 ok(0 == r, "expected 0, got %d\n", r);
8199 test_dm_messages(0, 0, 0);
8200
8201 DestroyWindow(hwRichEdit);
8202 DestroyWindow(hwParent);
8203 }
8204
test_EM_FINDWORDBREAK_W(void)8205 static void test_EM_FINDWORDBREAK_W(void)
8206 {
8207 static const struct {
8208 WCHAR c;
8209 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8210 } delimiter_tests[] = {
8211 {0x0a, FALSE}, /* newline */
8212 {0x0b, FALSE}, /* vertical tab */
8213 {0x0c, FALSE}, /* form feed */
8214 {0x0d, FALSE}, /* carriage return */
8215 {0x20, TRUE}, /* space */
8216 {0x61, FALSE}, /* capital letter a */
8217 {0xa0, FALSE}, /* no-break space */
8218 {0x2000, FALSE}, /* en quad */
8219 {0x3000, FALSE}, /* Ideographic space */
8220 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8221 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8222 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8223 {0xac00, FALSE}, /* Hangul character GA*/
8224 {0xd7af, FALSE}, /* End of Hangul character chart */
8225 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
8226 {0xff20, FALSE}, /* fullwidth commercial @ */
8227 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
8228 };
8229 int i;
8230 HWND hwndRichEdit = new_richeditW(NULL);
8231 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8232 for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8233 {
8234 WCHAR wbuf[2];
8235 int result;
8236
8237 wbuf[0] = delimiter_tests[i].c;
8238 wbuf[1] = 0;
8239 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8240 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8241 todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8242 ok(result == delimiter_tests[i].isdelimiter,
8243 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8244 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8245 }
8246 DestroyWindow(hwndRichEdit);
8247 }
8248
test_EM_FINDWORDBREAK_A(void)8249 static void test_EM_FINDWORDBREAK_A(void)
8250 {
8251 static const struct {
8252 WCHAR c;
8253 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8254 } delimiter_tests[] = {
8255 {0x0a, FALSE}, /* newline */
8256 {0x0b, FALSE}, /* vertical tab */
8257 {0x0c, FALSE}, /* form feed */
8258 {0x0d, FALSE}, /* carriage return */
8259 {0x20, TRUE}, /* space */
8260 {0x61, FALSE}, /* capital letter a */
8261 };
8262 int i;
8263 HWND hwndRichEdit = new_richedit(NULL);
8264
8265 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8266 for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8267 {
8268 int result;
8269 char buf[2];
8270 buf[0] = delimiter_tests[i].c;
8271 buf[1] = 0;
8272 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8273 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8274 todo_wine_if (buf[0] == 0x20)
8275 ok(result == delimiter_tests[i].isdelimiter,
8276 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8277 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8278 }
8279 DestroyWindow(hwndRichEdit);
8280 }
8281
format_test_result(char * target,const char * src)8282 static void format_test_result(char *target, const char *src)
8283 {
8284 int i;
8285 for (i = 0; i < strlen(src); i++)
8286 sprintf(target + 2*i, "%02x", src[i] & 0xFF);
8287 target[2*i] = 0;
8288 }
8289
8290 /*
8291 * This test attempts to show the effect of enter on a richedit
8292 * control v1.0 inserts CRLF whereas for higher versions it only
8293 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8294 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8295 * does for higher. The same test is cloned in riched32 and riched20.
8296 * Also shows the difference between WM_CHAR/WM_KEYDOWN in v1.0 and higher versions
8297 */
test_enter(void)8298 static void test_enter(void)
8299 {
8300 static const struct {
8301 const char *initialtext;
8302 const int cursor;
8303 const char *expectedwmtext;
8304 const char *expectedemtext;
8305 const char *expectedemtextcrlf;
8306 } testenteritems[] = {
8307 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8308 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8309 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8310 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8311 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8312 };
8313
8314 char expectedbuf[1024];
8315 char resultbuf[1024];
8316 HWND hwndRichEdit = new_richedit(NULL);
8317 UINT i;
8318 char buf[1024] = {0};
8319 GETTEXTEX getText = {sizeof(buf)};
8320 LRESULT result;
8321 const char *expected;
8322
8323 for (i = 0; i < ARRAY_SIZE(testenteritems); i++)
8324 {
8325 /* Set the text to the initial text */
8326 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8327 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8328
8329 /* Send Enter */
8330 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8331 simulate_typing_characters(hwndRichEdit, "\r");
8332
8333 /* 1. Retrieve with WM_GETTEXT */
8334 buf[0] = 0x00;
8335 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8336 expected = testenteritems[i].expectedwmtext;
8337
8338 format_test_result(resultbuf, buf);
8339 format_test_result(expectedbuf, expected);
8340
8341 result = strcmp(expected, buf);
8342 ok (result == 0,
8343 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8344 i, resultbuf, expectedbuf);
8345
8346 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8347 getText.flags = GT_DEFAULT;
8348 getText.codepage = CP_ACP;
8349 buf[0] = 0x00;
8350 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8351 expected = testenteritems[i].expectedemtext;
8352
8353 format_test_result(resultbuf, buf);
8354 format_test_result(expectedbuf, expected);
8355
8356 result = strcmp(expected, buf);
8357 ok (result == 0,
8358 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8359 i, resultbuf, expectedbuf);
8360
8361 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8362 getText.flags = GT_USECRLF;
8363 getText.codepage = CP_ACP;
8364 buf[0] = 0x00;
8365 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8366 expected = testenteritems[i].expectedemtextcrlf;
8367
8368 format_test_result(resultbuf, buf);
8369 format_test_result(expectedbuf, expected);
8370
8371 result = strcmp(expected, buf);
8372 ok (result == 0,
8373 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8374 i, resultbuf, expectedbuf);
8375 }
8376
8377 /* Show that WM_CHAR is handled differently from WM_KEYDOWN */
8378 getText.flags = GT_DEFAULT;
8379 getText.codepage = CP_ACP;
8380
8381 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8382 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8383 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8384 SendMessageW(hwndRichEdit, WM_KEYDOWN, VK_RETURN, 0);
8385
8386 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8387 ok(result == 2, "Got %d\n", (int)result);
8388 format_test_result(resultbuf, buf);
8389 format_test_result(expectedbuf, "T\r");
8390 result = strcmp(resultbuf, expectedbuf);
8391 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8392
8393 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8394 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8395 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8396 SendMessageW(hwndRichEdit, WM_CHAR, '\r', 0);
8397
8398 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8399 ok(result == 1, "Got %d\n", (int)result);
8400 format_test_result(resultbuf, buf);
8401 format_test_result(expectedbuf, "T");
8402 result = strcmp(resultbuf, expectedbuf);
8403 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8404
8405 DestroyWindow(hwndRichEdit);
8406 }
8407
test_WM_CREATE(void)8408 static void test_WM_CREATE(void)
8409 {
8410 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8411 static const char title[] = "line1\nline2";
8412
8413 HWND rich_edit;
8414 LRESULT res;
8415 char buf[64];
8416 int len;
8417
8418 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8419 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8420 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8421
8422 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8423 ok(len == 5, "GetWindowText returned %d\n", len);
8424 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8425
8426 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8427 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8428
8429 DestroyWindow(rich_edit);
8430
8431 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8432 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8433 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8434
8435 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8436 ok(len == 12, "GetWindowText returned %d\n", len);
8437 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8438
8439 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8440 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8441
8442 DestroyWindow(rich_edit);
8443 }
8444
8445 /*******************************************************************
8446 * Test that after deleting all of the text, the first paragraph
8447 * format reverts to the default.
8448 */
test_reset_default_para_fmt(void)8449 static void test_reset_default_para_fmt( void )
8450 {
8451 HWND richedit = new_richeditW( NULL );
8452 PARAFORMAT2 fmt;
8453 WORD def_align, new_align;
8454
8455 memset( &fmt, 0, sizeof(fmt) );
8456 fmt.cbSize = sizeof(PARAFORMAT2);
8457 fmt.dwMask = -1;
8458 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8459 def_align = fmt.wAlignment;
8460 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8461
8462 simulate_typing_characters( richedit, "123" );
8463
8464 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8465 fmt.dwMask = PFM_ALIGNMENT;
8466 fmt.wAlignment = new_align;
8467 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8468
8469 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8470 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8471
8472 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8473 SendMessageA( richedit, WM_CUT, 0, 0 );
8474
8475 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8476 ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8477
8478 DestroyWindow( richedit );
8479 }
8480
test_EM_SETREADONLY(void)8481 static void test_EM_SETREADONLY(void)
8482 {
8483 HWND richedit = new_richeditW(NULL);
8484 DWORD dwStyle;
8485 LRESULT res;
8486
8487 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8488 ok(res == 1, "EM_SETREADONLY\n");
8489 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8490 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8491
8492 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8493 ok(res == 1, "EM_SETREADONLY\n");
8494 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8495 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8496
8497 DestroyWindow(richedit);
8498 }
8499
twips2points(LONG value)8500 static inline LONG twips2points(LONG value)
8501 {
8502 return value / 20;
8503 }
8504
8505 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8506 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
_test_font_size(unsigned line,HWND hwnd,LONG size,LONG expected_size,LRESULT expected_res,BOOL expected_undo)8507 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8508 LRESULT expected_res, BOOL expected_undo)
8509 {
8510 CHARFORMAT2A cf;
8511 LRESULT res;
8512 BOOL isundo;
8513
8514 cf.cbSize = sizeof(cf);
8515 cf.dwMask = CFM_SIZE;
8516
8517 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8518 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8519 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8520 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8521 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8522 twips2points(cf.yHeight), expected_size);
8523 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8524 isundo, expected_undo);
8525 }
8526
test_EM_SETFONTSIZE(void)8527 static void test_EM_SETFONTSIZE(void)
8528 {
8529 HWND richedit = new_richedit(NULL);
8530 CHAR text[] = "wine";
8531 CHARFORMAT2A tmp_cf;
8532 LONG default_size;
8533
8534 tmp_cf.cbSize = sizeof(tmp_cf);
8535 tmp_cf.dwMask = CFM_SIZE;
8536 tmp_cf.yHeight = 9 * 20.0;
8537 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8538
8539 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8540
8541 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8542 /* without selection */
8543 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8544 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8545 default_size = twips2points(tmp_cf.yHeight);
8546 ok(default_size == 9, "Default font size should not be changed.\n");
8547 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8548
8549 SendMessageA(richedit, EM_SETSEL, 0, 2);
8550
8551 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8552
8553 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8554 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8555 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8556
8557 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8558 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8559 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8560 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8561 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8562 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8563 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8564 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8565 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8566 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8567 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8568 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8569 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8570 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8571 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8572 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8573 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8574 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8575 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8576 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8577 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8578 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8579 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8580 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8581 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8582 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8583 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8584 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8585
8586 /* return FALSE when richedit is TM_PLAINTEXT mode */
8587 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8588 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8589 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8590
8591 DestroyWindow(richedit);
8592 }
8593
test_alignment_style(void)8594 static void test_alignment_style(void)
8595 {
8596 HWND richedit = NULL;
8597 PARAFORMAT2 pf;
8598 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8599 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8600 ES_LEFT | ES_RIGHT | ES_CENTER};
8601 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8602 PFA_RIGHT, PFA_CENTER};
8603 const char * streamtext =
8604 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8605 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8606 "}\r\n";
8607 EDITSTREAM es;
8608 int i;
8609
8610 for (i = 0; i < ARRAY_SIZE(align_style); i++)
8611 {
8612 DWORD dwStyle, new_align;
8613
8614 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8615 memset(&pf, 0, sizeof(pf));
8616 pf.cbSize = sizeof(PARAFORMAT2);
8617 pf.dwMask = -1;
8618
8619 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8620 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8621 i, pf.wAlignment, align_mask[i]);
8622 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8623 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8624 "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8625
8626
8627 /* Based on test_reset_default_para_fmt() */
8628 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8629 simulate_typing_characters(richedit, "123");
8630
8631 SendMessageW(richedit, EM_SETSEL, 0, -1);
8632 pf.dwMask = PFM_ALIGNMENT;
8633 pf.wAlignment = new_align;
8634 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8635
8636 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8637 ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8638
8639 SendMessageW(richedit, EM_SETSEL, 0, -1);
8640 SendMessageW(richedit, WM_CUT, 0, 0);
8641
8642 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8643 ok(pf.wAlignment == align_mask[i], "got %d expect %d\n", pf.wAlignment, align_mask[i]);
8644
8645 /* Test out of bounds tab count */
8646 pf.dwMask = PFM_TABSTOPS;
8647 pf.cTabCount = -25000;
8648 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8649 ok(pf.cTabCount == -25000, "Got %d\n", pf.cTabCount);
8650 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8651 ok(pf.cTabCount == 0, "Got %d\n", pf.cTabCount);
8652 pf.dwMask = PFM_TABSTOPS;
8653 pf.cTabCount = 25000;
8654 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8655 ok(pf.cTabCount == 25000, "Got %d\n", pf.cTabCount);
8656 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8657 ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8658 pf.dwMask = PFM_TABSTOPS;
8659 pf.cTabCount = 32;
8660 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8661 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8662 ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8663 pf.dwMask = PFM_TABSTOPS;
8664 pf.cTabCount = 33;
8665 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8666 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8667 ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8668 pf.dwMask = PFM_TABSTOPS;
8669 pf.cTabCount = 1;
8670 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8671 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8672 ok(pf.cTabCount == 1, "Got %d\n", pf.cTabCount);
8673
8674 DestroyWindow(richedit);
8675 }
8676
8677 /* test with EM_STREAMIN */
8678 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8679 simulate_typing_characters(richedit, "abc");
8680 es.dwCookie = (DWORD_PTR)&streamtext;
8681 es.dwError = 0;
8682 es.pfnCallback = test_EM_STREAMIN_esCallback;
8683 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8684 SendMessageW(richedit, EM_SETSEL, 0, -1);
8685 memset(&pf, 0, sizeof(pf));
8686 pf.cbSize = sizeof(PARAFORMAT2);
8687 pf.dwMask = -1;
8688 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8689 ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8690 DestroyWindow(richedit);
8691 }
8692
test_WM_GETTEXTLENGTH(void)8693 static void test_WM_GETTEXTLENGTH(void)
8694 {
8695 HWND hwndRichEdit = new_richedit(NULL);
8696 static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8697 static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8698 static const char text3[] = "abcdef\x8e\xf0";
8699 int result;
8700
8701 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8702 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8703 ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8704 result, lstrlenA(text1));
8705
8706 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8707 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8708 ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8709 result, lstrlenA(text2));
8710
8711 /* Test with multibyte character */
8712 if (!is_lang_japanese)
8713 skip("Skip multibyte character tests on non-Japanese platform\n");
8714 else
8715 {
8716 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8717 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8718 todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8719 }
8720
8721 DestroyWindow(hwndRichEdit);
8722 }
8723
test_rtf(void)8724 static void test_rtf(void)
8725 {
8726 const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8727 "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8728 const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8729 0x201d,0x200e,0x200f,0x200d,0x200c};
8730 const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8731 const char *highlight = "{\\rtf1{\\colortbl;\\red0\\green0\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;}\\cf2\\highlight3 foo\\par}";
8732
8733 HWND edit = new_richeditW( NULL );
8734 EDITSTREAM es;
8735 WCHAR buf[80];
8736 LRESULT result;
8737 PARAFORMAT2 fmt;
8738 CHARFORMAT2W cf;
8739
8740 /* Test rtf specials */
8741 es.dwCookie = (DWORD_PTR)&specials;
8742 es.dwError = 0;
8743 es.pfnCallback = test_EM_STREAMIN_esCallback;
8744 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8745 ok( result == 11, "got %ld\n", result );
8746
8747 result = SendMessageW( edit, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf );
8748 ok( result == ARRAY_SIZE(expect_specials), "got %ld\n", result );
8749 ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8750
8751 /* Show that \rtlpar propagates to the second paragraph and is
8752 reset by \pard in the third. */
8753 es.dwCookie = (DWORD_PTR)&pard;
8754 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8755 ok( result == 11, "got %ld\n", result );
8756
8757 fmt.cbSize = sizeof(fmt);
8758 SendMessageW( edit, EM_SETSEL, 1, 1 );
8759 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8760 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8761 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8762 SendMessageW( edit, EM_SETSEL, 5, 5 );
8763 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8764 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8765 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8766 SendMessageW( edit, EM_SETSEL, 9, 9 );
8767 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8768 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8769 ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8770
8771 /* Test \highlight */
8772 es.dwCookie = (DWORD_PTR)&highlight;
8773 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8774 ok( result == 3, "got %ld\n", result );
8775 SendMessageW( edit, EM_SETSEL, 1, 1 );
8776 memset( &cf, 0, sizeof(cf) );
8777 cf.cbSize = sizeof(cf);
8778 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8779 ok( (cf.dwEffects & (CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR)) == 0, "got %08x\n", cf.dwEffects );
8780 ok( cf.crTextColor == RGB(128,128,128), "got %08x\n", cf.crTextColor );
8781 ok( cf.crBackColor == RGB(192,192,192), "got %08x\n", cf.crBackColor );
8782
8783 DestroyWindow( edit );
8784 }
8785
test_background(void)8786 static void test_background(void)
8787 {
8788 HWND hwndRichEdit = new_richedit(NULL);
8789
8790 /* set the background color to black */
8791 ValidateRect(hwndRichEdit, NULL);
8792 SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8793 ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8794
8795 DestroyWindow(hwndRichEdit);
8796 }
8797
test_eop_char_fmt(void)8798 static void test_eop_char_fmt(void)
8799 {
8800 HWND edit = new_richedit( NULL );
8801 const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8802 "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8803 "\\f0\\fs25 Second\\par"
8804 "{\\f0\\fs26 Third}\\par"
8805 "{\\f0\\fs22 Fourth}\\par}}}";
8806 EDITSTREAM es;
8807 CHARFORMAT2W cf;
8808 int i, num, expect_height;
8809
8810 es.dwCookie = (DWORD_PTR)&rtf;
8811 es.dwError = 0;
8812 es.pfnCallback = test_EM_STREAMIN_esCallback;
8813 num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8814 ok( num == 25, "got %d\n", num );
8815
8816 for (i = 0; i <= num; i++)
8817 {
8818 SendMessageW( edit, EM_SETSEL, i, i + 1 );
8819 cf.cbSize = sizeof(cf);
8820 cf.dwMask = CFM_SIZE;
8821 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8822 ok( cf.dwMask & CFM_SIZE, "%d: got %08x\n", i, cf.dwMask );
8823 if (i < 6) expect_height = 160;
8824 else if (i < 13) expect_height = 250;
8825 else if (i < 18) expect_height = 260;
8826 else if (i == 18 || i == 25) expect_height = 250;
8827 else expect_height = 220;
8828 ok( cf.yHeight == expect_height, "%d: got %d\n", i, cf.yHeight );
8829 }
8830
8831 DestroyWindow( edit );
8832 }
8833
test_para_numbering(void)8834 static void test_para_numbering(void)
8835 {
8836 HWND edit = new_richeditW( NULL );
8837 const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8838 "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8839 "\\fs20\\fi200\\li360\\f0 First\\par"
8840 "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
8841 "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
8842 const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
8843 EDITSTREAM es;
8844 WCHAR buf[80];
8845 LRESULT result;
8846 PARAFORMAT2 fmt, fmt2;
8847 GETTEXTEX get_text;
8848 CHARFORMAT2W cf;
8849
8850 get_text.cb = sizeof(buf);
8851 get_text.flags = GT_RAWTEXT;
8852 get_text.codepage = 1200;
8853 get_text.lpDefaultChar = NULL;
8854 get_text.lpUsedDefChar = NULL;
8855
8856 es.dwCookie = (DWORD_PTR)&numbers;
8857 es.dwError = 0;
8858 es.pfnCallback = test_EM_STREAMIN_esCallback;
8859 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8860 ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8861
8862 result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
8863 ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8864 ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
8865
8866 SendMessageW( edit, EM_SETSEL, 1, 1 );
8867 memset( &fmt, 0, sizeof(fmt) );
8868 fmt.cbSize = sizeof(fmt);
8869 fmt.dwMask = PFM_ALL2;
8870 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8871 ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
8872 ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
8873 ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
8874 ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
8875 ok( fmt.dxStartIndent == 560, "got %d\n", fmt.dxStartIndent );
8876 ok( fmt.dxOffset == -200, "got %d\n", fmt.dxOffset );
8877
8878 /* Second para should have identical fmt */
8879 SendMessageW( edit, EM_SETSEL, 10, 10 );
8880 memset( &fmt2, 0, sizeof(fmt2) );
8881 fmt2.cbSize = sizeof(fmt2);
8882 fmt2.dwMask = PFM_ALL2;
8883 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
8884 ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
8885
8886 /* Check the eop heights - this determines the label height */
8887 SendMessageW( edit, EM_SETSEL, 12, 13 );
8888 cf.cbSize = sizeof(cf);
8889 cf.dwMask = CFM_SIZE;
8890 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8891 ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8892
8893 SendMessageW( edit, EM_SETSEL, 18, 19 );
8894 cf.cbSize = sizeof(cf);
8895 cf.dwMask = CFM_SIZE;
8896 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8897 ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8898
8899 DestroyWindow( edit );
8900 }
8901
fill_reobject_struct(REOBJECT * reobj,LONG cp,LPOLEOBJECT poleobj,LPSTORAGE pstg,LPOLECLIENTSITE polesite,LONG sizel_cx,LONG sizel_cy,DWORD aspect,DWORD flags,DWORD user)8902 static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
8903 LPSTORAGE pstg, LPOLECLIENTSITE polesite, LONG sizel_cx,
8904 LONG sizel_cy, DWORD aspect, DWORD flags, DWORD user)
8905 {
8906 reobj->cbStruct = sizeof(*reobj);
8907 reobj->clsid = CLSID_NULL;
8908 reobj->cp = cp;
8909 reobj->poleobj = poleobj;
8910 reobj->pstg = pstg;
8911 reobj->polesite = polesite;
8912 reobj->sizel.cx = sizel_cx;
8913 reobj->sizel.cy = sizel_cy;
8914 reobj->dvaspect = aspect;
8915 reobj->dwFlags = flags;
8916 reobj->dwUser = user;
8917 }
8918
test_EM_SELECTIONTYPE(void)8919 static void test_EM_SELECTIONTYPE(void)
8920 {
8921 HWND hwnd = new_richedit(NULL);
8922 IRichEditOle *reole = NULL;
8923 static const char text1[] = "abcdefg\n";
8924 int result;
8925 REOBJECT reo1, reo2;
8926 IOleClientSite *clientsite;
8927 HRESULT hr;
8928
8929 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text1);
8930 SendMessageA(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&reole);
8931
8932 SendMessageA(hwnd, EM_SETSEL, 1, 1);
8933 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8934 ok(result == SEL_EMPTY, "got wrong selection type: %x.\n", result);
8935
8936 SendMessageA(hwnd, EM_SETSEL, 1, 2);
8937 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8938 ok(result == SEL_TEXT, "got wrong selection type: %x.\n", result);
8939
8940 SendMessageA(hwnd, EM_SETSEL, 2, 5);
8941 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8942 ok(result == (SEL_TEXT | SEL_MULTICHAR), "got wrong selection type: %x.\n", result);
8943
8944 SendMessageA(hwnd, EM_SETSEL, 0, 1);
8945 hr = IRichEditOle_GetClientSite(reole, &clientsite);
8946 ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8947 fill_reobject_struct(&reo1, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8948 DVASPECT_CONTENT, 0, 1);
8949 hr = IRichEditOle_InsertObject(reole, &reo1);
8950 ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8951 IOleClientSite_Release(clientsite);
8952
8953 SendMessageA(hwnd, EM_SETSEL, 0, 1);
8954 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8955 ok(result == SEL_OBJECT, "got wrong selection type: %x.\n", result);
8956
8957 SendMessageA(hwnd, EM_SETSEL, 0, 2);
8958 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8959 ok(result == (SEL_TEXT | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8960
8961 SendMessageA(hwnd, EM_SETSEL, 0, 3);
8962 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8963 ok(result == (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8964
8965 SendMessageA(hwnd, EM_SETSEL, 2, 3);
8966 hr = IRichEditOle_GetClientSite(reole, &clientsite);
8967 ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8968 fill_reobject_struct(&reo2, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8969 DVASPECT_CONTENT, 0, 2);
8970 hr = IRichEditOle_InsertObject(reole, &reo2);
8971 ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8972 IOleClientSite_Release(clientsite);
8973
8974 SendMessageA(hwnd, EM_SETSEL, 0, 2);
8975 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8976 ok(result == (SEL_OBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8977
8978 SendMessageA(hwnd, EM_SETSEL, 0, 3);
8979 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8980 ok(result == (SEL_OBJECT | SEL_MULTIOBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8981
8982 SendMessageA(hwnd, EM_SETSEL, 0, 4);
8983 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8984 ok(result == (SEL_TEXT| SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT), "got wrong selection type: %x.\n", result);
8985
8986 IRichEditOle_Release(reole);
8987 DestroyWindow(hwnd);
8988 }
8989
test_window_classes(void)8990 static void test_window_classes(void)
8991 {
8992 static const struct
8993 {
8994 const char *class;
8995 BOOL success;
8996 } test[] =
8997 {
8998 { "RichEdit", FALSE },
8999 { "RichEdit20A", TRUE },
9000 { "RichEdit20W", TRUE },
9001 { "RichEdit50A", FALSE },
9002 { "RichEdit50W", FALSE }
9003 };
9004 int i;
9005 HWND hwnd;
9006
9007 for (i = 0; i < sizeof(test)/sizeof(test[0]); i++)
9008 {
9009 SetLastError(0xdeadbeef);
9010 hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
9011 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
9012 ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
9013 test[i].class, test[i].success ? "succeed" : "fail");
9014 if (!hwnd)
9015 todo_wine
9016 ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %d\n", GetLastError());
9017 else
9018 DestroyWindow(hwnd);
9019 }
9020 }
9021
START_TEST(editor)9022 START_TEST( editor )
9023 {
9024 BOOL ret;
9025 /* Must explicitly LoadLibrary(). The test has no references to functions in
9026 * RICHED20.DLL, so the linker doesn't actually link to it. */
9027 hmoduleRichEdit = LoadLibraryA("riched20.dll");
9028 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
9029 is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
9030
9031 test_window_classes();
9032 test_WM_CHAR();
9033 test_EM_FINDTEXT(FALSE);
9034 test_EM_FINDTEXT(TRUE);
9035 test_EM_GETLINE();
9036 test_EM_POSFROMCHAR();
9037 test_EM_SCROLLCARET();
9038 test_EM_SCROLL();
9039 test_scrollbar_visibility();
9040 test_WM_SETTEXT();
9041 test_EM_LINELENGTH();
9042 test_EM_SETCHARFORMAT();
9043 test_EM_SETTEXTMODE();
9044 test_TM_PLAINTEXT();
9045 test_EM_SETOPTIONS();
9046 test_WM_GETTEXT();
9047 test_EM_GETTEXTRANGE();
9048 test_EM_GETSELTEXT();
9049 test_EM_SETUNDOLIMIT();
9050 test_ES_PASSWORD();
9051 test_EM_SETTEXTEX();
9052 test_EM_LIMITTEXT();
9053 test_EM_EXLIMITTEXT();
9054 test_EM_GETLIMITTEXT();
9055 test_WM_SETFONT();
9056 test_EM_GETMODIFY();
9057 test_EM_SETSEL();
9058 test_EM_EXSETSEL();
9059 test_WM_PASTE();
9060 test_EM_STREAMIN();
9061 test_EM_STREAMOUT();
9062 test_EM_STREAMOUT_FONTTBL();
9063 test_EM_STREAMOUT_empty_para();
9064 test_EM_StreamIn_Undo();
9065 test_EM_FORMATRANGE();
9066 test_unicode_conversions();
9067 test_EM_GETTEXTLENGTHEX();
9068 test_WM_GETTEXTLENGTH();
9069 test_EM_REPLACESEL(1);
9070 test_EM_REPLACESEL(0);
9071 test_WM_NOTIFY();
9072 test_EN_LINK();
9073 test_EM_AUTOURLDETECT();
9074 test_eventMask();
9075 test_undo_coalescing();
9076 test_word_movement();
9077 test_EM_CHARFROMPOS();
9078 test_SETPARAFORMAT();
9079 test_word_wrap();
9080 test_autoscroll();
9081 test_format_rect();
9082 test_WM_GETDLGCODE();
9083 test_zoom();
9084 test_dialogmode();
9085 test_EM_FINDWORDBREAK_W();
9086 test_EM_FINDWORDBREAK_A();
9087 test_enter();
9088 test_WM_CREATE();
9089 test_reset_default_para_fmt();
9090 test_EM_SETREADONLY();
9091 test_EM_SETFONTSIZE();
9092 test_alignment_style();
9093 test_rtf();
9094 test_background();
9095 test_eop_char_fmt();
9096 test_para_numbering();
9097 test_EM_SELECTIONTYPE();
9098
9099 /* Set the environment variable WINETEST_RICHED20 to keep windows
9100 * responsive and open for 30 seconds. This is useful for debugging.
9101 */
9102 if (getenv( "WINETEST_RICHED20" )) {
9103 keep_responsive(30);
9104 }
9105
9106 OleFlushClipboard();
9107 ret = FreeLibrary(hmoduleRichEdit);
9108 ok(ret, "error: %d\n", (int) GetLastError());
9109 }
9110