sync to trunk head (37079)
[reactos.git] / rostests / winetests / riched20 / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
34
35 static HMODULE hmoduleRichEdit;
36
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 HWND hwnd;
39 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41 hmoduleRichEdit, NULL);
42 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
43 return hwnd;
44 }
45
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
48 }
49
50 static void processPendingMessages(void)
51 {
52 MSG msg;
53 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
54 TranslateMessage(&msg);
55 DispatchMessage(&msg);
56 }
57 }
58
59 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
60 {
61 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
62 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
63 SetFocus(hwnd);
64 keybd_event(mod_vk, mod_scan_code, 0, 0);
65 keybd_event(vk, scan_code, 0, 0);
66 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
67 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
68 processPendingMessages();
69 }
70
71 static void simulate_typing_characters(HWND hwnd, const char* szChars)
72 {
73 int ret;
74
75 while (*szChars != '\0') {
76 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
77 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
78 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
79 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
80 szChars++;
81 }
82 }
83
84 static const char haystack[] = "WINEWine wineWine wine WineWine";
85 /* ^0 ^10 ^20 ^30 */
86
87 struct find_s {
88 int start;
89 int end;
90 const char *needle;
91 int flags;
92 int expected_loc;
93 int _todo_wine;
94 };
95
96
97 struct find_s find_tests[] = {
98 /* Find in empty text */
99 {0, -1, "foo", FR_DOWN, -1, 0},
100 {0, -1, "foo", 0, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {20, 5, "foo", FR_DOWN, -1, 0},
103 {5, 20, "foo", FR_DOWN, -1, 0}
104 };
105
106 struct find_s find_tests2[] = {
107 /* No-result find */
108 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
109 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
110
111 /* Subsequent finds */
112 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
113 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
114 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
115 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
116
117 /* Find backwards */
118 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
119 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
120 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
121
122 /* Case-insensitive */
123 {1, 31, "wInE", FR_DOWN, 4, 0},
124 {1, 31, "Wine", FR_DOWN, 4, 0},
125
126 /* High-to-low ranges */
127 {20, 5, "Wine", FR_DOWN, -1, 0},
128 {2, 1, "Wine", FR_DOWN, -1, 0},
129 {30, 29, "Wine", FR_DOWN, -1, 0},
130 {20, 5, "Wine", 0, 13, 0},
131
132 /* Find nothing */
133 {5, 10, "", FR_DOWN, -1, 0},
134 {10, 5, "", FR_DOWN, -1, 0},
135 {0, -1, "", FR_DOWN, -1, 0},
136 {10, 5, "", 0, -1, 0},
137
138 /* Whole-word search */
139 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
140 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
141 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
142 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
143 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
144 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
145 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
146
147 /* Bad ranges */
148 {5, 200, "XXX", FR_DOWN, -1, 0},
149 {-20, 20, "Wine", FR_DOWN, -1, 0},
150 {-20, 20, "Wine", FR_DOWN, -1, 0},
151 {-15, -20, "Wine", FR_DOWN, -1, 0},
152 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
153
154 /* Check the case noted in bug 4479 where matches at end aren't recognized */
155 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
156 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
157 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
158 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
159 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
160
161 /* The backwards case of bug 4479; bounds look right
162 * Fails because backward find is wrong */
163 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
164 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
165
166 {0, -1, "wineWine wine", 0, -1, 0},
167 };
168
169 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
170 int findloc;
171 FINDTEXT ft;
172 memset(&ft, 0, sizeof(ft));
173 ft.chrg.cpMin = f->start;
174 ft.chrg.cpMax = f->end;
175 ft.lpstrText = f->needle;
176 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
177 ok(findloc == f->expected_loc,
178 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
179 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
180 }
181
182 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
183 int id) {
184 int findloc;
185 FINDTEXTEX ft;
186 int expected_end_loc;
187
188 memset(&ft, 0, sizeof(ft));
189 ft.chrg.cpMin = f->start;
190 ft.chrg.cpMax = f->end;
191 ft.lpstrText = f->needle;
192 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
193 ok(findloc == f->expected_loc,
194 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
195 name, id, f->needle, f->start, f->end, f->flags, findloc);
196 ok(ft.chrgText.cpMin == f->expected_loc,
197 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
198 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
199 expected_end_loc = ((f->expected_loc == -1) ? -1
200 : f->expected_loc + strlen(f->needle));
201 ok(ft.chrgText.cpMax == expected_end_loc,
202 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
203 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
204 }
205
206 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
207 int num_tests)
208 {
209 int i;
210
211 for (i = 0; i < num_tests; i++) {
212 if (find[i]._todo_wine) {
213 todo_wine {
214 check_EM_FINDTEXT(hwnd, name, &find[i], i);
215 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
216 }
217 } else {
218 check_EM_FINDTEXT(hwnd, name, &find[i], i);
219 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
220 }
221 }
222 }
223
224 static void test_EM_FINDTEXT(void)
225 {
226 HWND hwndRichEdit = new_richedit(NULL);
227 CHARFORMAT2 cf2;
228
229 /* Empty rich edit control */
230 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
231 sizeof(find_tests)/sizeof(struct find_s));
232
233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
234
235 /* Haystack text */
236 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
237 sizeof(find_tests2)/sizeof(struct find_s));
238
239 /* Setting a format on an arbitrary range should have no effect in search
240 results. This tests correct offset reporting across runs. */
241 cf2.cbSize = sizeof(CHARFORMAT2);
242 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
243 (LPARAM) &cf2);
244 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
245 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
246 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
247 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
248
249 /* Haystack text, again */
250 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
251 sizeof(find_tests2)/sizeof(struct find_s));
252
253 /* Yet another range */
254 cf2.dwMask = CFM_BOLD | cf2.dwMask;
255 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
256 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
257 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
258
259 /* Haystack text, again */
260 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
261 sizeof(find_tests2)/sizeof(struct find_s));
262
263 DestroyWindow(hwndRichEdit);
264 }
265
266 static const struct getline_s {
267 int line;
268 size_t buffer_len;
269 const char *text;
270 } gl[] = {
271 {0, 10, "foo bar\r"},
272 {1, 10, "\r"},
273 {2, 10, "bar\r"},
274 {3, 10, "\r"},
275
276 /* Buffer smaller than line length */
277 {0, 2, "foo bar\r"},
278 {0, 1, "foo bar\r"},
279 {0, 0, "foo bar\r"}
280 };
281
282 static void test_EM_GETLINE(void)
283 {
284 int i;
285 HWND hwndRichEdit = new_richedit(NULL);
286 static const int nBuf = 1024;
287 char dest[1024], origdest[1024];
288 const char text[] = "foo bar\n"
289 "\n"
290 "bar\n";
291
292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
293
294 memset(origdest, 0xBB, nBuf);
295 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
296 {
297 int nCopied;
298 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
299 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
300 memset(dest, 0xBB, nBuf);
301 *(WORD *) dest = gl[i].buffer_len;
302
303 /* EM_GETLINE appends a "\r\0" to the end of the line
304 * nCopied counts up to and including the '\r' */
305 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
306 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
307 expected_nCopied);
308 /* two special cases since a parameter is passed via dest */
309 if (gl[i].buffer_len == 0)
310 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
311 "buffer_len=0\n");
312 else if (gl[i].buffer_len == 1)
313 ok(dest[0] == gl[i].text[0] && !dest[1] &&
314 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
315 else
316 {
317 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
318 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
319 ok(!strncmp(dest + expected_bytes_written, origdest
320 + expected_bytes_written, nBuf - expected_bytes_written),
321 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
322 }
323 }
324
325 DestroyWindow(hwndRichEdit);
326 }
327
328 static void test_EM_LINELENGTH(void)
329 {
330 HWND hwndRichEdit = new_richedit(NULL);
331 const char * text =
332 "richedit1\r"
333 "richedit1\n"
334 "richedit1\r\n"
335 "richedit1";
336 int offset_test[10][2] = {
337 {0, 9},
338 {5, 9},
339 {10, 9},
340 {15, 9},
341 {20, 9},
342 {25, 9},
343 {30, 9},
344 {35, 9},
345 {40, 0},
346 {45, 0},
347 };
348 int i;
349 LRESULT result;
350
351 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
352
353 for (i = 0; i < 10; i++) {
354 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
355 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
356 offset_test[i][0], result, offset_test[i][1]);
357 }
358
359 DestroyWindow(hwndRichEdit);
360 }
361
362 static int get_scroll_pos_y(HWND hwnd)
363 {
364 POINT p = {-1, -1};
365 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
366 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
367 return p.y;
368 }
369
370 static void move_cursor(HWND hwnd, long charindex)
371 {
372 CHARRANGE cr;
373 cr.cpMax = charindex;
374 cr.cpMin = charindex;
375 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
376 }
377
378 static void line_scroll(HWND hwnd, int amount)
379 {
380 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
381 }
382
383 static void test_EM_SCROLLCARET(void)
384 {
385 int prevY, curY;
386 HWND hwndRichEdit = new_richedit(NULL);
387 const char text[] = "aa\n"
388 "this is a long line of text that should be longer than the "
389 "control's width\n"
390 "cc\n"
391 "dd\n"
392 "ee\n"
393 "ff\n"
394 "gg\n"
395 "hh\n";
396
397 /* Can't verify this */
398 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
399
400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
401
402 /* Caret above visible window */
403 line_scroll(hwndRichEdit, 3);
404 prevY = get_scroll_pos_y(hwndRichEdit);
405 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
406 curY = get_scroll_pos_y(hwndRichEdit);
407 ok(prevY != curY, "%d == %d\n", prevY, curY);
408
409 /* Caret below visible window */
410 move_cursor(hwndRichEdit, sizeof(text) - 1);
411 line_scroll(hwndRichEdit, -3);
412 prevY = get_scroll_pos_y(hwndRichEdit);
413 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
414 curY = get_scroll_pos_y(hwndRichEdit);
415 ok(prevY != curY, "%d == %d\n", prevY, curY);
416
417 /* Caret in visible window */
418 move_cursor(hwndRichEdit, sizeof(text) - 2);
419 prevY = get_scroll_pos_y(hwndRichEdit);
420 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
421 curY = get_scroll_pos_y(hwndRichEdit);
422 ok(prevY == curY, "%d != %d\n", prevY, curY);
423
424 /* Caret still in visible window */
425 line_scroll(hwndRichEdit, -1);
426 prevY = get_scroll_pos_y(hwndRichEdit);
427 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
428 curY = get_scroll_pos_y(hwndRichEdit);
429 ok(prevY == curY, "%d != %d\n", prevY, curY);
430
431 DestroyWindow(hwndRichEdit);
432 }
433
434 static void test_EM_POSFROMCHAR(void)
435 {
436 HWND hwndRichEdit = new_richedit(NULL);
437 int i;
438 LRESULT result;
439 unsigned int height = 0;
440 int xpos = 0;
441 static const char text[] = "aa\n"
442 "this is a long line of text that should be longer than the "
443 "control's width\n"
444 "cc\n"
445 "dd\n"
446 "ee\n"
447 "ff\n"
448 "gg\n"
449 "hh\n";
450
451 /* Fill the control to lines to ensure that most of them are offscreen */
452 for (i = 0; i < 50; i++)
453 {
454 /* Do not modify the string; it is exactly 16 characters long. */
455 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
456 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
457 }
458
459 /*
460 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
461 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
462 Richedit 3.0 accepts either of the above API conventions.
463 */
464
465 /* Testing Richedit 2.0 API format */
466
467 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
468 Since all lines are identical and drawn with the same font,
469 they should have the same height... right?
470 */
471 for (i = 0; i < 50; i++)
472 {
473 /* All the lines are 16 characters long */
474 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
475 if (i == 0)
476 {
477 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
478 todo_wine {
479 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
480 }
481 xpos = LOWORD(result);
482 }
483 else if (i == 1)
484 {
485 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
486 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
487 height = HIWORD(result);
488 }
489 else
490 {
491 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
492 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
493 }
494 }
495
496 /* Testing position at end of text */
497 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
498 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
499 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
500
501 /* Testing position way past end of text */
502 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
503 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
504 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
505
506 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
507 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
508 for (i = 0; i < 50; i++)
509 {
510 /* All the lines are 16 characters long */
511 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
512 ok((signed short)(HIWORD(result)) == (i - 1) * height,
513 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
514 (signed short)(HIWORD(result)), (i - 1) * height);
515 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
516 }
517
518 /* Testing position at end of text */
519 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
520 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
521 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
522
523 /* Testing position way past end of text */
524 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
525 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
526 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
527
528 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
530 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
531
532 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
533 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
534 todo_wine {
535 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
536 }
537 xpos = LOWORD(result);
538
539 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
540 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
541 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
542 todo_wine {
543 /* Fails on builtin because horizontal scrollbar is not being shown */
544 ok((signed short)(LOWORD(result)) < xpos,
545 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
546 (signed short)(LOWORD(result)), xpos);
547 }
548 DestroyWindow(hwndRichEdit);
549 }
550
551 static void test_EM_SETCHARFORMAT(void)
552 {
553 HWND hwndRichEdit = new_richedit(NULL);
554 CHARFORMAT2 cf2;
555 int rc = 0;
556 int tested_effects[] = {
557 CFE_BOLD,
558 CFE_ITALIC,
559 CFE_UNDERLINE,
560 CFE_STRIKEOUT,
561 CFE_PROTECTED,
562 CFE_LINK,
563 CFE_SUBSCRIPT,
564 CFE_SUPERSCRIPT,
565 0
566 };
567 int i;
568 CHARRANGE cr;
569
570 /* Invalid flags, CHARFORMAT2 structure blanked out */
571 memset(&cf2, 0, sizeof(cf2));
572 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
573 (LPARAM) &cf2);
574 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
575
576 /* A valid flag, CHARFORMAT2 structure blanked out */
577 memset(&cf2, 0, sizeof(cf2));
578 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
579 (LPARAM) &cf2);
580 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
581
582 /* A valid flag, CHARFORMAT2 structure blanked out */
583 memset(&cf2, 0, sizeof(cf2));
584 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
585 (LPARAM) &cf2);
586 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
587
588 /* A valid flag, CHARFORMAT2 structure blanked out */
589 memset(&cf2, 0, sizeof(cf2));
590 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
591 (LPARAM) &cf2);
592 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
593
594 /* A valid flag, CHARFORMAT2 structure blanked out */
595 memset(&cf2, 0, sizeof(cf2));
596 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
597 (LPARAM) &cf2);
598 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
599
600 /* Invalid flags, CHARFORMAT2 structure minimally filled */
601 memset(&cf2, 0, sizeof(cf2));
602 cf2.cbSize = sizeof(CHARFORMAT2);
603 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
604 (LPARAM) &cf2);
605 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
606 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
607 ok(rc == FALSE, "Should not be able to undo here.\n");
608 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
609
610 /* A valid flag, CHARFORMAT2 structure minimally filled */
611 memset(&cf2, 0, sizeof(cf2));
612 cf2.cbSize = sizeof(CHARFORMAT2);
613 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
614 (LPARAM) &cf2);
615 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
616 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
617 todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
618 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
619
620 /* A valid flag, CHARFORMAT2 structure minimally filled */
621 memset(&cf2, 0, sizeof(cf2));
622 cf2.cbSize = sizeof(CHARFORMAT2);
623 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
624 (LPARAM) &cf2);
625 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
626 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
627 ok(rc == FALSE, "Should not be able to undo here.\n");
628 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
629
630 /* A valid flag, CHARFORMAT2 structure minimally filled */
631 memset(&cf2, 0, sizeof(cf2));
632 cf2.cbSize = sizeof(CHARFORMAT2);
633 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
634 (LPARAM) &cf2);
635 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
636 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
637 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
638 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
639
640 /* A valid flag, CHARFORMAT2 structure minimally filled */
641 memset(&cf2, 0, sizeof(cf2));
642 cf2.cbSize = sizeof(CHARFORMAT2);
643 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
644 (LPARAM) &cf2);
645 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
646 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
647 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
648 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
649
650 cf2.cbSize = sizeof(CHARFORMAT2);
651 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
652 (LPARAM) &cf2);
653
654 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
655 cf2.cbSize = sizeof(CHARFORMAT2);
656 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
657 (LPARAM) &cf2);
658 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
659 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
660
661 /* wParam==0 is default char format, does not set modify */
662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
663 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
664 ok(rc == 0, "Text marked as modified, expected not modified!\n");
665 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
666 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
667 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
668 ok(rc == 0, "Text marked as modified, expected not modified!\n");
669
670 /* wParam==SCF_SELECTION sets modify if nonempty selection */
671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673 ok(rc == 0, "Text marked as modified, expected not modified!\n");
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
675 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677 ok(rc == 0, "Text marked as modified, expected not modified!\n");
678
679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
680 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
681 ok(rc == 0, "Text marked as modified, expected not modified!\n");
682 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
683 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
684 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
685 ok(rc == 0, "Text marked as modified, expected not modified!\n");
686 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
687 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
688 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
689 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
691
692 /* wParam==SCF_ALL sets modify regardless of whether text is present */
693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
694 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
695 ok(rc == 0, "Text marked as modified, expected not modified!\n");
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
697 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
700
701 DestroyWindow(hwndRichEdit);
702
703 /* EM_GETCHARFORMAT tests */
704 for (i = 0; tested_effects[i]; i++)
705 {
706 hwndRichEdit = new_richedit(NULL);
707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
708
709 /* Need to set a TrueType font to get consistent CFM_BOLD results */
710 memset(&cf2, 0, sizeof(CHARFORMAT2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
713 cf2.dwEffects = 0;
714 strcpy(cf2.szFaceName, "Courier New");
715 cf2.wWeight = FW_DONTCARE;
716 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
717
718 memset(&cf2, 0, sizeof(CHARFORMAT2));
719 cf2.cbSize = sizeof(CHARFORMAT2);
720 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
721 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
722 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
723 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
724 ||
725 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
726 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
727 ok((cf2.dwEffects & tested_effects[i]) == 0,
728 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
729
730 memset(&cf2, 0, sizeof(CHARFORMAT2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 cf2.dwMask = tested_effects[i];
733 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
734 cf2.dwMask = CFM_SUPERSCRIPT;
735 cf2.dwEffects = tested_effects[i];
736 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
737 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
738
739 memset(&cf2, 0, sizeof(CHARFORMAT2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
742 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
743 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
744 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
745 ||
746 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
747 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
748 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
749 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
750
751 memset(&cf2, 0, sizeof(CHARFORMAT2));
752 cf2.cbSize = sizeof(CHARFORMAT2);
753 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
754 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
755 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
756 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
757 ||
758 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
759 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
760 ok((cf2.dwEffects & tested_effects[i]) == 0,
761 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
762
763 memset(&cf2, 0, sizeof(CHARFORMAT2));
764 cf2.cbSize = sizeof(CHARFORMAT2);
765 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
766 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
767 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
768 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
769 ||
770 (cf2.dwMask & tested_effects[i]) == 0),
771 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
772
773 DestroyWindow(hwndRichEdit);
774 }
775
776 for (i = 0; tested_effects[i]; i++)
777 {
778 hwndRichEdit = new_richedit(NULL);
779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
780
781 /* Need to set a TrueType font to get consistent CFM_BOLD results */
782 memset(&cf2, 0, sizeof(CHARFORMAT2));
783 cf2.cbSize = sizeof(CHARFORMAT2);
784 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
785 cf2.dwEffects = 0;
786 strcpy(cf2.szFaceName, "Courier New");
787 cf2.wWeight = FW_DONTCARE;
788 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
789
790 memset(&cf2, 0, sizeof(CHARFORMAT2));
791 cf2.cbSize = sizeof(CHARFORMAT2);
792 cf2.dwMask = tested_effects[i];
793 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
794 cf2.dwMask = CFM_SUPERSCRIPT;
795 cf2.dwEffects = tested_effects[i];
796 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
797 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
798
799 memset(&cf2, 0, sizeof(CHARFORMAT2));
800 cf2.cbSize = sizeof(CHARFORMAT2);
801 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
802 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
803 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
804 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
805 ||
806 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
807 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
808 ok((cf2.dwEffects & tested_effects[i]) == 0,
809 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
810
811 memset(&cf2, 0, sizeof(CHARFORMAT2));
812 cf2.cbSize = sizeof(CHARFORMAT2);
813 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
814 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
815 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
816 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
817 ||
818 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
819 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
820 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
821 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
822
823 memset(&cf2, 0, sizeof(CHARFORMAT2));
824 cf2.cbSize = sizeof(CHARFORMAT2);
825 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
826 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
827 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
828 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
829 ||
830 (cf2.dwMask & tested_effects[i]) == 0),
831 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
832 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
833 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
834
835 DestroyWindow(hwndRichEdit);
836 }
837
838 /* Effects applied on an empty selection should take effect when selection is
839 replaced with text */
840 hwndRichEdit = new_richedit(NULL);
841 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
842 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
843
844 memset(&cf2, 0, sizeof(CHARFORMAT2));
845 cf2.cbSize = sizeof(CHARFORMAT2);
846 cf2.dwMask = CFM_BOLD;
847 cf2.dwEffects = CFE_BOLD;
848 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
849
850 /* Selection is now nonempty */
851 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
852
853 memset(&cf2, 0, sizeof(CHARFORMAT2));
854 cf2.cbSize = sizeof(CHARFORMAT2);
855 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
856 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857
858 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
859 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
860 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
861 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
862
863
864 /* Set two effects on an empty selection */
865 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
866 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
867
868 memset(&cf2, 0, sizeof(CHARFORMAT2));
869 cf2.cbSize = sizeof(CHARFORMAT2);
870 cf2.dwMask = CFM_BOLD;
871 cf2.dwEffects = CFE_BOLD;
872 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
873 cf2.dwMask = CFM_ITALIC;
874 cf2.dwEffects = CFE_ITALIC;
875 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
876
877 /* Selection is now nonempty */
878 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
879
880 memset(&cf2, 0, sizeof(CHARFORMAT2));
881 cf2.cbSize = sizeof(CHARFORMAT2);
882 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
883 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
884
885 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
886 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
887 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
888 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
889
890 /* Setting the (empty) selection to exactly the same place as before should
891 NOT clear the insertion style! */
892 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
893 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
894
895 memset(&cf2, 0, sizeof(CHARFORMAT2));
896 cf2.cbSize = sizeof(CHARFORMAT2);
897 cf2.dwMask = CFM_BOLD;
898 cf2.dwEffects = CFE_BOLD;
899 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
900
901 /* Empty selection in same place, insert style should NOT be forgotten here. */
902 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
903
904 /* Selection is now nonempty */
905 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
906
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
910 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
911
912 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
913 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
914 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
915 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
916
917 /* Ditto with EM_EXSETSEL */
918 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
919 cr.cpMin = 2; cr.cpMax = 2;
920 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
921
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 cf2.dwMask = CFM_BOLD;
925 cf2.dwEffects = CFE_BOLD;
926 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
927
928 /* Empty selection in same place, insert style should NOT be forgotten here. */
929 cr.cpMin = 2; cr.cpMax = 2;
930 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
931
932 /* Selection is now nonempty */
933 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
934
935 memset(&cf2, 0, sizeof(CHARFORMAT2));
936 cf2.cbSize = sizeof(CHARFORMAT2);
937 cr.cpMin = 2; cr.cpMax = 6;
938 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
939 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
940
941 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
942 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
943 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
944 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
945
946 DestroyWindow(hwndRichEdit);
947 }
948
949 static void test_EM_SETTEXTMODE(void)
950 {
951 HWND hwndRichEdit = new_richedit(NULL);
952 CHARFORMAT2 cf2, cf2test;
953 CHARRANGE cr;
954 int rc = 0;
955
956 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
957 /*Insert text into the control*/
958
959 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
960
961 /*Attempt to change the control to plain text mode*/
962 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
963 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
964
965 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
966 If rich text is pasted, it should have the same formatting as the rest
967 of the text in the control*/
968
969 /*Italicize the text
970 *NOTE: If the default text was already italicized, the test will simply
971 reverse; in other words, it will copy a regular "wine" into a plain
972 text window that uses an italicized format*/
973 cf2.cbSize = sizeof(CHARFORMAT2);
974 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
975 (LPARAM) &cf2);
976
977 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
978 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
979
980 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
981 ok(rc == 0, "Text marked as modified, expected not modified!\n");
982
983 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
984 however, SCF_ALL has been implemented*/
985 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
986 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
987
988 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
989 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
990
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
992
993 /*Select the string "wine"*/
994 cr.cpMin = 0;
995 cr.cpMax = 4;
996 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
997
998 /*Copy the italicized "wine" to the clipboard*/
999 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1000
1001 /*Reset the formatting to default*/
1002 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1003 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1004 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1005
1006 /*Clear the text in the control*/
1007 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1008
1009 /*Switch to Plain Text Mode*/
1010 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1011 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1012
1013 /*Input "wine" again in normal format*/
1014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1015
1016 /*Paste the italicized "wine" into the control*/
1017 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1018
1019 /*Select a character from the first "wine" string*/
1020 cr.cpMin = 2;
1021 cr.cpMax = 3;
1022 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1023
1024 /*Retrieve its formatting*/
1025 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1026 (LPARAM) &cf2);
1027
1028 /*Select a character from the second "wine" string*/
1029 cr.cpMin = 5;
1030 cr.cpMax = 6;
1031 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1032
1033 /*Retrieve its formatting*/
1034 cf2test.cbSize = sizeof(CHARFORMAT2);
1035 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1036 (LPARAM) &cf2test);
1037
1038 /*Compare the two formattings*/
1039 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1040 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1041 cf2.dwEffects, cf2test.dwEffects);
1042 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1043 printing "wine" in the current format(normal)
1044 pasting "wine" from the clipboard(italicized)
1045 comparing the two formats(should differ)*/
1046
1047 /*Attempt to switch with text in control*/
1048 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1049 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1050
1051 /*Clear control*/
1052 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1053
1054 /*Switch into Rich Text mode*/
1055 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1056 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1057
1058 /*Print "wine" in normal formatting into the control*/
1059 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1060
1061 /*Paste italicized "wine" into the control*/
1062 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1063
1064 /*Select text from the first "wine" string*/
1065 cr.cpMin = 1;
1066 cr.cpMax = 3;
1067 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1068
1069 /*Retrieve its formatting*/
1070 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1071 (LPARAM) &cf2);
1072
1073 /*Select text from the second "wine" string*/
1074 cr.cpMin = 6;
1075 cr.cpMax = 7;
1076 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1077
1078 /*Retrieve its formatting*/
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1080 (LPARAM) &cf2test);
1081
1082 /*Test that the two formattings are not the same*/
1083 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1084 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1085 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1086
1087 DestroyWindow(hwndRichEdit);
1088 }
1089
1090 static void test_TM_PLAINTEXT(void)
1091 {
1092 /*Tests plain text properties*/
1093
1094 HWND hwndRichEdit = new_richedit(NULL);
1095 CHARFORMAT2 cf2, cf2test;
1096 CHARRANGE cr;
1097 int rc = 0;
1098
1099 /*Switch to plain text mode*/
1100
1101 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1102 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1103
1104 /*Fill control with text*/
1105
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1107
1108 /*Select some text and bold it*/
1109
1110 cr.cpMin = 10;
1111 cr.cpMax = 20;
1112 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113 cf2.cbSize = sizeof(CHARFORMAT2);
1114 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1115 (LPARAM) &cf2);
1116
1117 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1118 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1119
1120 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1121 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1122
1123 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1124 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1125
1126 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1127 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1128
1129 /*Get the formatting of those characters*/
1130
1131 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1132
1133 /*Get the formatting of some other characters*/
1134 cf2test.cbSize = sizeof(CHARFORMAT2);
1135 cr.cpMin = 21;
1136 cr.cpMax = 30;
1137 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1138 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1139
1140 /*Test that they are the same as plain text allows only one formatting*/
1141
1142 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1143 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1144 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1145
1146 /*Fill the control with a "wine" string, which when inserted will be bold*/
1147
1148 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1149
1150 /*Copy the bolded "wine" string*/
1151
1152 cr.cpMin = 0;
1153 cr.cpMax = 4;
1154 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1155 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1156
1157 /*Swap back to rich text*/
1158
1159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1160 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1161
1162 /*Set the default formatting to bold italics*/
1163
1164 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1165 cf2.dwMask |= CFM_ITALIC;
1166 cf2.dwEffects ^= CFE_ITALIC;
1167 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1168 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1169
1170 /*Set the text in the control to "wine", which will be bold and italicized*/
1171
1172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1173
1174 /*Paste the plain text "wine" string, which should take the insert
1175 formatting, which at the moment is bold italics*/
1176
1177 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1178
1179 /*Select the first "wine" string and retrieve its formatting*/
1180
1181 cr.cpMin = 1;
1182 cr.cpMax = 3;
1183 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1185
1186 /*Select the second "wine" string and retrieve its formatting*/
1187
1188 cr.cpMin = 5;
1189 cr.cpMax = 7;
1190 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1191 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1192
1193 /*Compare the two formattings. They should be the same.*/
1194
1195 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1196 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1197 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1198 DestroyWindow(hwndRichEdit);
1199 }
1200
1201 static void test_WM_GETTEXT(void)
1202 {
1203 HWND hwndRichEdit = new_richedit(NULL);
1204 static const char text[] = "Hello. My name is RichEdit!";
1205 static const char text2[] = "Hello. My name is RichEdit!\r";
1206 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1207 char buffer[1024] = {0};
1208 int result;
1209
1210 /* Baseline test with normal-sized buffer */
1211 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1212 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1213 ok(result == lstrlen(buffer),
1214 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1215 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1216 result = strcmp(buffer,text);
1217 ok(result == 0,
1218 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1219
1220 /* Test for returned value of WM_GETTEXTLENGTH */
1221 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1222 ok(result == lstrlen(text),
1223 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1224 result, lstrlen(text));
1225
1226 /* Test for behavior in overflow case */
1227 memset(buffer, 0, 1024);
1228 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1229 ok(result == 0 ||
1230 result == lstrlenA(text) - 1, /* XP, win2k3 */
1231 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1232 result = strcmp(buffer,text);
1233 if (result)
1234 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1235 ok(result == 0,
1236 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1237
1238 /* Baseline test with normal-sized buffer and carriage return */
1239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1240 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1241 ok(result == lstrlen(buffer),
1242 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1243 result = strcmp(buffer,text2_after);
1244 ok(result == 0,
1245 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1246
1247 /* Test for returned value of WM_GETTEXTLENGTH */
1248 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1249 ok(result == lstrlen(text2_after),
1250 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1251 result, lstrlen(text2_after));
1252
1253 /* Test for behavior of CRLF conversion in case of overflow */
1254 memset(buffer, 0, 1024);
1255 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1256 ok(result == 0 ||
1257 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1258 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1259 result = strcmp(buffer,text2);
1260 if (result)
1261 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1262 ok(result == 0,
1263 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1264
1265 DestroyWindow(hwndRichEdit);
1266 }
1267
1268 static void test_EM_GETTEXTRANGE(void)
1269 {
1270 HWND hwndRichEdit = new_richedit(NULL);
1271 const char * text1 = "foo bar\r\nfoo bar";
1272 const char * text2 = "foo bar\rfoo bar";
1273 const char * expect = "bar\rfoo";
1274 char buffer[1024] = {0};
1275 LRESULT result;
1276 TEXTRANGEA textRange;
1277
1278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1279
1280 textRange.lpstrText = buffer;
1281 textRange.chrg.cpMin = 4;
1282 textRange.chrg.cpMax = 11;
1283 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1284 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1285 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1286
1287 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1288
1289 textRange.lpstrText = buffer;
1290 textRange.chrg.cpMin = 4;
1291 textRange.chrg.cpMax = 11;
1292 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1293 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1294 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1295
1296 DestroyWindow(hwndRichEdit);
1297 }
1298
1299 static void test_EM_GETSELTEXT(void)
1300 {
1301 HWND hwndRichEdit = new_richedit(NULL);
1302 const char * text1 = "foo bar\r\nfoo bar";
1303 const char * text2 = "foo bar\rfoo bar";
1304 const char * expect = "bar\rfoo";
1305 char buffer[1024] = {0};
1306 LRESULT result;
1307
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1309
1310 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1311 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1312 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1313 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1314
1315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1316
1317 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1318 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1319 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1320 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1321
1322 DestroyWindow(hwndRichEdit);
1323 }
1324
1325 /* FIXME: need to test unimplemented options and robustly test wparam */
1326 static void test_EM_SETOPTIONS(void)
1327 {
1328 HWND hwndRichEdit = new_richedit(NULL);
1329 static const char text[] = "Hello. My name is RichEdit!";
1330 char buffer[1024] = {0};
1331
1332 /* NEGATIVE TESTING - NO OPTIONS SET */
1333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1334 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1335
1336 /* testing no readonly by sending 'a' to the control*/
1337 SetFocus(hwndRichEdit);
1338 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1339 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(buffer[0]=='a',
1341 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1342 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1343
1344 /* READONLY - sending 'a' to the control */
1345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1346 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1347 SetFocus(hwndRichEdit);
1348 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1349 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1350 ok(buffer[0]==text[0],
1351 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1352
1353 DestroyWindow(hwndRichEdit);
1354 }
1355
1356 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1357 {
1358 CHARFORMAT2W text_format;
1359 text_format.cbSize = sizeof(text_format);
1360 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1361 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1362 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1363 }
1364
1365 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1366 {
1367 int link_present = 0;
1368
1369 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1370 if (is_url)
1371 { /* control text is url; should get CFE_LINK */
1372 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1373 }
1374 else
1375 {
1376 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1377 }
1378 }
1379
1380 static HWND new_static_wnd(HWND parent) {
1381 return new_window("Static", 0, parent);
1382 }
1383
1384 static void test_EM_AUTOURLDETECT(void)
1385 {
1386 struct urls_s {
1387 const char *text;
1388 int is_url;
1389 } urls[12] = {
1390 {"winehq.org", 0},
1391 {"http://www.winehq.org", 1},
1392 {"http//winehq.org", 0},
1393 {"ww.winehq.org", 0},
1394 {"www.winehq.org", 1},
1395 {"ftp://192.168.1.1", 1},
1396 {"ftp//192.168.1.1", 0},
1397 {"mailto:your@email.com", 1},
1398 {"prospero:prosperoserver", 1},
1399 {"telnet:test", 1},
1400 {"news:newserver", 1},
1401 {"wais:waisserver", 1}
1402 };
1403
1404 int i, j;
1405 int urlRet=-1;
1406 HWND hwndRichEdit, parent;
1407
1408 /* All of the following should cause the URL to be detected */
1409 const char * templates_delim[] = {
1410 "This is some text with X on it",
1411 "This is some text with (X) on it",
1412 "This is some text with X\r on it",
1413 "This is some text with ---X--- on it",
1414 "This is some text with \"X\" on it",
1415 "This is some text with 'X' on it",
1416 "This is some text with 'X' on it",
1417 "This is some text with :X: on it",
1418
1419 "This text ends with X",
1420
1421 "This is some text with X) on it",
1422 "This is some text with X--- on it",
1423 "This is some text with X\" on it",
1424 "This is some text with X' on it",
1425 "This is some text with X: on it",
1426
1427 "This is some text with (X on it",
1428 "This is some text with \rX on it",
1429 "This is some text with ---X on it",
1430 "This is some text with \"X on it",
1431 "This is some text with 'X on it",
1432 "This is some text with :X on it",
1433 };
1434 /* None of these should cause the URL to be detected */
1435 const char * templates_non_delim[] = {
1436 "This is some text with |X| on it",
1437 "This is some text with *X* on it",
1438 "This is some text with /X/ on it",
1439 "This is some text with +X+ on it",
1440 "This is some text with %X% on it",
1441 "This is some text with #X# on it",
1442 "This is some text with @X@ on it",
1443 "This is some text with \\X\\ on it",
1444 "This is some text with |X on it",
1445 "This is some text with *X on it",
1446 "This is some text with /X on it",
1447 "This is some text with +X on it",
1448 "This is some text with %X on it",
1449 "This is some text with #X on it",
1450 "This is some text with @X on it",
1451 "This is some text with \\X on it",
1452 };
1453 /* All of these cause the URL detection to be extended by one more byte,
1454 thus demonstrating that the tested character is considered as part
1455 of the URL. */
1456 const char * templates_xten_delim[] = {
1457 "This is some text with X| on it",
1458 "This is some text with X* on it",
1459 "This is some text with X/ on it",
1460 "This is some text with X+ on it",
1461 "This is some text with X% on it",
1462 "This is some text with X# on it",
1463 "This is some text with X@ on it",
1464 "This is some text with X\\ on it",
1465 };
1466 char buffer[1024];
1467
1468 parent = new_static_wnd(NULL);
1469 hwndRichEdit = new_richedit(parent);
1470 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1471 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1472 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1473 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1474 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1475 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1476 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1477 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1478 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1479 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1480 /* for each url, check the text to see if CFE_LINK effect is present */
1481 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1482
1483 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1484 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1485 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1486
1487 /* Link detection should happen immediately upon WM_SETTEXT */
1488 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1489 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1490 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1491 }
1492 DestroyWindow(hwndRichEdit);
1493
1494 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1495 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1496 hwndRichEdit = new_richedit(parent);
1497
1498 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1499 char * at_pos;
1500 int at_offset;
1501 int end_offset;
1502
1503 at_pos = strchr(templates_delim[j], 'X');
1504 at_offset = at_pos - templates_delim[j];
1505 strncpy(buffer, templates_delim[j], at_offset);
1506 buffer[at_offset] = '\0';
1507 strcat(buffer, urls[i].text);
1508 strcat(buffer, templates_delim[j] + at_offset + 1);
1509 end_offset = at_offset + strlen(urls[i].text);
1510
1511 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1512 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1513
1514 /* This assumes no templates start with the URL itself, and that they
1515 have at least two characters before the URL text */
1516 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1517 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1518 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1519 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1520 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1521 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1522
1523 if (urls[i].is_url)
1524 {
1525 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1526 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1527 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1528 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1529 }
1530 else
1531 {
1532 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1533 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1534 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1535 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1536 }
1537 if (buffer[end_offset] != '\0')
1538 {
1539 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1540 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1541 if (buffer[end_offset +1] != '\0')
1542 {
1543 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1544 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1545 }
1546 }
1547 }
1548
1549 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1550 char * at_pos;
1551 int at_offset;
1552 int end_offset;
1553
1554 at_pos = strchr(templates_non_delim[j], 'X');
1555 at_offset = at_pos - templates_non_delim[j];
1556 strncpy(buffer, templates_non_delim[j], at_offset);
1557 buffer[at_offset] = '\0';
1558 strcat(buffer, urls[i].text);
1559 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1560 end_offset = at_offset + strlen(urls[i].text);
1561
1562 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1564
1565 /* This assumes no templates start with the URL itself, and that they
1566 have at least two characters before the URL text */
1567 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1568 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1569 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1570 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1571 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1572 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1573
1574 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1576 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1578 if (buffer[end_offset] != '\0')
1579 {
1580 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1581 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1582 if (buffer[end_offset +1] != '\0')
1583 {
1584 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1585 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1586 }
1587 }
1588 }
1589
1590 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1591 char * at_pos;
1592 int at_offset;
1593 int end_offset;
1594
1595 at_pos = strchr(templates_xten_delim[j], 'X');
1596 at_offset = at_pos - templates_xten_delim[j];
1597 strncpy(buffer, templates_xten_delim[j], at_offset);
1598 buffer[at_offset] = '\0';
1599 strcat(buffer, urls[i].text);
1600 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1601 end_offset = at_offset + strlen(urls[i].text);
1602
1603 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1605
1606 /* This assumes no templates start with the URL itself, and that they
1607 have at least two characters before the URL text */
1608 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1609 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1610 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1611 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1612 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1613 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1614
1615 if (urls[i].is_url)
1616 {
1617 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1618 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1619 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1620 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1621 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1622 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1623 }
1624 else
1625 {
1626 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1627 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1628 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1629 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1630 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1631 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1632 }
1633 if (buffer[end_offset +1] != '\0')
1634 {
1635 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1636 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1637 if (buffer[end_offset +2] != '\0')
1638 {
1639 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1640 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1641 }
1642 }
1643 }
1644
1645 DestroyWindow(hwndRichEdit);
1646 hwndRichEdit = NULL;
1647 }
1648
1649 /* Test detection of URLs within normal text - WM_CHAR case. */
1650 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1651 hwndRichEdit = new_richedit(parent);
1652
1653 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1654 char * at_pos;
1655 int at_offset;
1656 int end_offset;
1657 int u, v;
1658
1659 at_pos = strchr(templates_delim[j], 'X');
1660 at_offset = at_pos - templates_delim[j];
1661 end_offset = at_offset + strlen(urls[i].text);
1662
1663 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1664 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1665 for (u = 0; templates_delim[j][u]; u++) {
1666 if (templates_delim[j][u] == '\r') {
1667 simulate_typing_characters(hwndRichEdit, "\r");
1668 } else if (templates_delim[j][u] != 'X') {
1669 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1670 } else {
1671 for (v = 0; urls[i].text[v]; v++) {
1672 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1673 }
1674 }
1675 }
1676 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1677 trace("Using template: %s\n", templates_delim[j]);
1678
1679 /* This assumes no templates start with the URL itself, and that they
1680 have at least two characters before the URL text */
1681 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1682 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1683 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1684 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1685 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1687
1688 if (urls[i].is_url)
1689 {
1690 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1691 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1692 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1693 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1694 }
1695 else
1696 {
1697 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1698 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1699 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1700 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1701 }
1702 if (buffer[end_offset] != '\0')
1703 {
1704 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1706 if (buffer[end_offset +1] != '\0')
1707 {
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1710 }
1711 }
1712
1713 /* The following will insert a paragraph break after the first character
1714 of the URL candidate, thus breaking the URL. It is expected that the
1715 CFE_LINK attribute should break across both pieces of the URL */
1716 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1717 simulate_typing_characters(hwndRichEdit, "\r");
1718 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1719
1720 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1721 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1722 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1723 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1726
1727 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1728 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1729 /* end_offset moved because of paragraph break */
1730 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1731 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1732 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1733 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1734 {
1735 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1736 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1737 if (buffer[end_offset +2] != '\0')
1738 {
1739 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1740 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1741 }
1742 }
1743
1744 /* The following will remove the just-inserted paragraph break, thus
1745 restoring the URL */
1746 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1747 simulate_typing_characters(hwndRichEdit, "\b");
1748 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1749
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1756
1757 if (urls[i].is_url)
1758 {
1759 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1760 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1761 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1762 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1763 }
1764 else
1765 {
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1770 }
1771 if (buffer[end_offset] != '\0')
1772 {
1773 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1774 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1775 if (buffer[end_offset +1] != '\0')
1776 {
1777 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1778 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1779 }
1780 }
1781 }
1782 DestroyWindow(hwndRichEdit);
1783 hwndRichEdit = NULL;
1784 }
1785
1786 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1787 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1788 SETTEXTEX st;
1789
1790 hwndRichEdit = new_richedit(parent);
1791
1792 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1793 be detected:
1794 1) Set entire text, a la WM_SETTEXT
1795 2) Set a selection of the text to the URL
1796 3) Set a portion of the text at a time, which eventually results in
1797 an URL
1798 All of them should give equivalent results
1799 */
1800
1801 /* Set entire text in one go, like WM_SETTEXT */
1802 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1803 char * at_pos;
1804 int at_offset;
1805 int end_offset;
1806
1807 st.codepage = CP_ACP;
1808 st.flags = ST_DEFAULT;
1809
1810 at_pos = strchr(templates_delim[j], 'X');
1811 at_offset = at_pos - templates_delim[j];
1812 strncpy(buffer, templates_delim[j], at_offset);
1813 buffer[at_offset] = '\0';
1814 strcat(buffer, urls[i].text);
1815 strcat(buffer, templates_delim[j] + at_offset + 1);
1816 end_offset = at_offset + strlen(urls[i].text);
1817
1818 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1819 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1820
1821 /* This assumes no templates start with the URL itself, and that they
1822 have at least two characters before the URL text */
1823 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1824 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1827 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1828 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1829
1830 if (urls[i].is_url)
1831 {
1832 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1833 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1834 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1835 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1836 }
1837 else
1838 {
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1841 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1842 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1843 }
1844 if (buffer[end_offset] != '\0')
1845 {
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1848 if (buffer[end_offset +1] != '\0')
1849 {
1850 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1851 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1852 }
1853 }
1854 }
1855
1856 /* Set selection with X to the URL */
1857 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1858 char * at_pos;
1859 int at_offset;
1860 int end_offset;
1861
1862 at_pos = strchr(templates_delim[j], 'X');
1863 at_offset = at_pos - templates_delim[j];
1864 end_offset = at_offset + strlen(urls[i].text);
1865
1866 st.codepage = CP_ACP;
1867 st.flags = ST_DEFAULT;
1868 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1869 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1870 st.flags = ST_SELECTION;
1871 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1872 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1873 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1874
1875 /* This assumes no templates start with the URL itself, and that they
1876 have at least two characters before the URL text */
1877 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1878 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1883
1884 if (urls[i].is_url)
1885 {
1886 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1887 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1888 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1889 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1890 }
1891 else
1892 {
1893 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1894 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1897 }
1898 if (buffer[end_offset] != '\0')
1899 {
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1902 if (buffer[end_offset +1] != '\0')
1903 {
1904 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1905 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1906 }
1907 }
1908 }
1909
1910 /* Set selection with X to the first character of the URL, then the rest */
1911 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1912 char * at_pos;
1913 int at_offset;
1914 int end_offset;
1915
1916 at_pos = strchr(templates_delim[j], 'X');
1917 at_offset = at_pos - templates_delim[j];
1918 end_offset = at_offset + strlen(urls[i].text);
1919
1920 strcpy(buffer, "YY");
1921 buffer[0] = urls[i].text[0];
1922
1923 st.codepage = CP_ACP;
1924 st.flags = ST_DEFAULT;
1925 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1926 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1927 st.flags = ST_SELECTION;
1928 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1929 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1930 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1932 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1933
1934 /* This assumes no templates start with the URL itself, and that they
1935 have at least two characters before the URL text */
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1938 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1939 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1942
1943 if (urls[i].is_url)
1944 {
1945 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1946 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1947 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1948 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1949 }
1950 else
1951 {
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1956 }
1957 if (buffer[end_offset] != '\0')
1958 {
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1961 if (buffer[end_offset +1] != '\0')
1962 {
1963 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1964 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1965 }
1966 }
1967 }
1968
1969 DestroyWindow(hwndRichEdit);
1970 hwndRichEdit = NULL;
1971 }
1972
1973 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1974 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1975 hwndRichEdit = new_richedit(parent);
1976
1977 /* Set selection with X to the URL */
1978 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1979 char * at_pos;
1980 int at_offset;
1981 int end_offset;
1982
1983 at_pos = strchr(templates_delim[j], 'X');
1984 at_offset = at_pos - templates_delim[j];
1985 end_offset = at_offset + strlen(urls[i].text);
1986
1987 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1989 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1990 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1991 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1992
1993 /* This assumes no templates start with the URL itself, and that they
1994 have at least two characters before the URL text */
1995 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1996 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1997 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1998 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2001
2002 if (urls[i].is_url)
2003 {
2004 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2005 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2006 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2007 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2008 }
2009 else
2010 {
2011 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2012 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2013 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2014 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 }
2016 if (buffer[end_offset] != '\0')
2017 {
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2020 if (buffer[end_offset +1] != '\0')
2021 {
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2024 }
2025 }
2026 }
2027
2028 /* Set selection with X to the first character of the URL, then the rest */
2029 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2030 char * at_pos;
2031 int at_offset;
2032 int end_offset;
2033
2034 at_pos = strchr(templates_delim[j], 'X');
2035 at_offset = at_pos - templates_delim[j];
2036 end_offset = at_offset + strlen(urls[i].text);
2037
2038 strcpy(buffer, "YY");
2039 buffer[0] = urls[i].text[0];
2040
2041 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2042 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2043 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2044 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2045 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2046 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2047 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2048
2049 /* This assumes no templates start with the URL itself, and that they
2050 have at least two characters before the URL text */
2051 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2052 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2057
2058 if (urls[i].is_url)
2059 {
2060 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2061 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2062 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2063 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2064 }
2065 else
2066 {
2067 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2068 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2069 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2070 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2071 }
2072 if (buffer[end_offset] != '\0')
2073 {
2074 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2075 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2076 if (buffer[end_offset +1] != '\0')
2077 {
2078 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2079 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2080 }
2081 }
2082 }
2083
2084 DestroyWindow(hwndRichEdit);
2085 hwndRichEdit = NULL;
2086 }
2087
2088 DestroyWindow(parent);
2089 }
2090
2091 static void test_EM_SCROLL(void)
2092 {
2093 int i, j;
2094 int r; /* return value */
2095 int expr; /* expected return value */
2096 HWND hwndRichEdit = new_richedit(NULL);
2097 int y_before, y_after; /* units of lines of text */
2098
2099 /* test a richedit box containing a single line of text */
2100 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2101 expr = 0x00010000;
2102 for (i = 0; i < 4; i++) {
2103 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2104
2105 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2106 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2107 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2108 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2109 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2110 "(i == %d)\n", y_after, i);
2111 }
2112
2113 /*
2114 * test a richedit box that will scroll. There are two general
2115 * cases: the case without any long lines and the case with a long
2116 * line.
2117 */
2118 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2119 if (i == 0)
2120 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2121 else
2122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2123 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2124 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2125 "LONG LINE \nb\nc\nd\ne");
2126 for (j = 0; j < 12; j++) /* reset scroll position to top */
2127 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2128
2129 /* get first visible line */
2130 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2131 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2132
2133 /* get new current first visible line */
2134 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2135
2136 ok(((r & 0xffffff00) == 0x00010000) &&
2137 ((r & 0x000000ff) != 0x00000000),
2138 "EM_SCROLL page down didn't scroll by a small positive number of "
2139 "lines (r == 0x%08x)\n", r);
2140 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2141 "(line %d scrolled to line %d\n", y_before, y_after);
2142
2143 y_before = y_after;
2144
2145 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2146 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2147 ok(((r & 0xffffff00) == 0x0001ff00),
2148 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2149 "(r == 0x%08x)\n", r);
2150 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2151 "%d scrolled to line %d\n", y_before, y_after);
2152
2153 y_before = y_after;
2154
2155 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2156
2157 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2158
2159 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2160 "(r == 0x%08x)\n", r);
2161 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2162 "1 line (%d scrolled to %d)\n", y_before, y_after);
2163
2164 y_before = y_after;
2165
2166 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2167
2168 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2169
2170 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2171 "(r == 0x%08x)\n", r);
2172 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2173 "line (%d scrolled to %d)\n", y_before, y_after);
2174
2175 y_before = y_after;
2176
2177 r = SendMessage(hwndRichEdit, EM_SCROLL,
2178 SB_LINEUP, 0); /* lineup beyond top */
2179
2180 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2181
2182 ok(r == 0x00010000,
2183 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2184 ok(y_before == y_after,
2185 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2186
2187 y_before = y_after;
2188
2189 r = SendMessage(hwndRichEdit, EM_SCROLL,
2190 SB_PAGEUP, 0);/*page up beyond top */
2191
2192 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2193
2194 ok(r == 0x00010000,
2195 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2196 ok(y_before == y_after,
2197 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2198
2199 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2200 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2201 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2202 r = SendMessage(hwndRichEdit, EM_SCROLL,
2203 SB_PAGEDOWN, 0); /* page down beyond bot */
2204 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2205
2206 ok(r == 0x00010000,
2207 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2208 ok(y_before == y_after,
2209 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2210 y_before, y_after);
2211
2212 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213 SendMessage(hwndRichEdit, EM_SCROLL,
2214 SB_LINEDOWN, 0); /* line down beyond bot */
2215 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2216
2217 ok(r == 0x00010000,
2218 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2219 ok(y_before == y_after,
2220 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2221 y_before, y_after);
2222 }
2223 DestroyWindow(hwndRichEdit);
2224 }
2225
2226 static void test_EM_SETUNDOLIMIT(void)
2227 {
2228 /* cases we test for:
2229 * default behaviour - limiting at 100 undo's
2230 * undo disabled - setting a limit of 0
2231 * undo limited - undo limit set to some to some number, like 2
2232 * bad input - sending a negative number should default to 100 undo's */
2233
2234 HWND hwndRichEdit = new_richedit(NULL);
2235 CHARRANGE cr;
2236 int i;
2237 int result;
2238
2239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2240 cr.cpMin = 0;
2241 cr.cpMax = 1;
2242 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2243 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2244 also, multiple pastes don't combine like WM_CHAR would */
2245 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2246
2247 /* first case - check the default */
2248 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2249 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2250 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2251 for (i=0; i<100; i++) /* Undo 100 of them */
2252 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2253 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2254 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2255
2256 /* second case - cannot undo */
2257 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2258 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2259 SendMessage(hwndRichEdit,
2260 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2261 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2262 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2263
2264 /* third case - set it to an arbitrary number */
2265 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2266 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2267 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2268 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2269 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2270 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2271 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2272 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2273 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2274 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2275 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2276 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2277 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2278 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2279
2280 /* fourth case - setting negative numbers should default to 100 undos */
2281 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2282 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2283 ok (result == 100,
2284 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2285
2286 DestroyWindow(hwndRichEdit);
2287 }
2288
2289 static void test_ES_PASSWORD(void)
2290 {
2291 /* This isn't hugely testable, so we're just going to run it through its paces */
2292
2293 HWND hwndRichEdit = new_richedit(NULL);
2294 WCHAR result;
2295
2296 /* First, check the default of a regular control */
2297 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2298 ok (result == 0,
2299 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2300
2301 /* Now, set it to something normal */
2302 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2303 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2304 ok (result == 120,
2305 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2306
2307 /* Now, set it to something odd */
2308 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2309 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2310 ok (result == 1234,
2311 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2312 DestroyWindow(hwndRichEdit);
2313 }
2314
2315 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2316 LPBYTE pbBuff,
2317 LONG cb,
2318 LONG *pcb)
2319 {
2320 char** str = (char**)dwCookie;
2321 *pcb = cb;
2322 if (*pcb > 0) {
2323 memcpy(*str, pbBuff, *pcb);
2324 *str += *pcb;
2325 }
2326 return 0;
2327 }
2328
2329 static void test_WM_SETTEXT()
2330 {
2331 HWND hwndRichEdit = new_richedit(NULL);
2332 const char * TestItem1 = "TestSomeText";
2333 const char * TestItem2 = "TestSomeText\r";
2334 const char * TestItem2_after = "TestSomeText\r\n";
2335 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2336 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2337 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2338 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2339 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2340 const char * TestItem5_after = "TestSomeText TestSomeText";
2341 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2342 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2343 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2344 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2345
2346 char buf[1024] = {0};
2347 LRESULT result;
2348 EDITSTREAM es;
2349 char * p;
2350
2351 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2352 any solitary \r to be converted to \r\n on return. Properly paired
2353 \r\n are not affected. It also shows that the special sequence \r\r\n
2354 gets converted to a single space.
2355 */
2356
2357 #define TEST_SETTEXT(a, b) \
2358 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2359 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2360 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2361 ok (result == lstrlen(buf), \
2362 "WM_GETTEXT returned %ld instead of expected %u\n", \
2363 result, lstrlen(buf)); \
2364 result = strcmp(b, buf); \
2365 ok(result == 0, \
2366 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2367
2368 TEST_SETTEXT(TestItem1, TestItem1)
2369 TEST_SETTEXT(TestItem2, TestItem2_after)
2370 TEST_SETTEXT(TestItem3, TestItem3_after)
2371 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2372 TEST_SETTEXT(TestItem4, TestItem4_after)
2373 TEST_SETTEXT(TestItem5, TestItem5_after)
2374 TEST_SETTEXT(TestItem6, TestItem6_after)
2375 TEST_SETTEXT(TestItem7, TestItem7_after)
2376
2377 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2378 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2379 p = buf;
2380 es.dwCookie = (DWORD_PTR)&p;
2381 es.dwError = 0;
2382 es.pfnCallback = test_WM_SETTEXT_esCallback;
2383 memset(buf, 0, sizeof(buf));
2384 SendMessage(hwndRichEdit, EM_STREAMOUT,
2385 (WPARAM)(SF_RTF), (LPARAM)&es);
2386 trace("EM_STREAMOUT produced: \n%s\n", buf);
2387 TEST_SETTEXT(buf, TestItem1)
2388
2389 #undef TEST_SETTEXT
2390 DestroyWindow(hwndRichEdit);
2391 }
2392
2393 static void test_EM_STREAMOUT(void)
2394 {
2395 HWND hwndRichEdit = new_richedit(NULL);
2396 int r;
2397 EDITSTREAM es;
2398 char buf[1024] = {0};
2399 char * p;
2400
2401 const char * TestItem1 = "TestSomeText";
2402 const char * TestItem2 = "TestSomeText\r";
2403 const char * TestItem3 = "TestSomeText\r\n";
2404
2405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2406 p = buf;
2407 es.dwCookie = (DWORD_PTR)&p;
2408 es.dwError = 0;
2409 es.pfnCallback = test_WM_SETTEXT_esCallback;
2410 memset(buf, 0, sizeof(buf));
2411 SendMessage(hwndRichEdit, EM_STREAMOUT,
2412 (WPARAM)(SF_TEXT), (LPARAM)&es);
2413 r = strlen(buf);
2414 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2415 ok(strcmp(buf, TestItem1) == 0,
2416 "streamed text different, got %s\n", buf);
2417
2418 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2419 p = buf;
2420 es.dwCookie = (DWORD_PTR)&p;
2421 es.dwError = 0;
2422 es.pfnCallback = test_WM_SETTEXT_esCallback;
2423 memset(buf, 0, sizeof(buf));
2424 SendMessage(hwndRichEdit, EM_STREAMOUT,
2425 (WPARAM)(SF_TEXT), (LPARAM)&es);
2426 r = strlen(buf);
2427 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2428 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2429 ok(strcmp(buf, TestItem3) == 0,
2430 "streamed text different from, got %s\n", buf);
2431 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2432 p = buf;
2433 es.dwCookie = (DWORD_PTR)&p;
2434 es.dwError = 0;
2435 es.pfnCallback = test_WM_SETTEXT_esCallback;
2436 memset(buf, 0, sizeof(buf));
2437 SendMessage(hwndRichEdit, EM_STREAMOUT,
2438 (WPARAM)(SF_TEXT), (LPARAM)&es);
2439 r = strlen(buf);
2440 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2441 ok(strcmp(buf, TestItem3) == 0,
2442 "streamed text different, got %s\n", buf);
2443
2444 DestroyWindow(hwndRichEdit);
2445 }
2446
2447 static void test_EM_SETTEXTEX(void)
2448 {
2449 HWND hwndRichEdit = new_richedit(NULL);
2450 SETTEXTEX setText;
2451 GETTEXTEX getText;
2452 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2453 'S', 'o', 'm', 'e',
2454 'T', 'e', 'x', 't', 0};
2455 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2456 'S', 'o', 'm', 'e',
2457 'T', 'e', 'x', 't',
2458 '\r', 0};
2459 const char * TestItem2_after = "TestSomeText\r\n";
2460 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2461 'S', 'o', 'm', 'e',
2462 'T', 'e', 'x', 't',
2463 '\r','\n','\r','\n', 0};
2464 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2465 'S', 'o', 'm', 'e',
2466 'T', 'e', 'x', 't',
2467 '\n','\n', 0};
2468 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2469 'S', 'o', 'm', 'e',
2470 'T', 'e', 'x', 't',
2471 '\r','\r', 0};
2472 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2473 'S', 'o', 'm', 'e',
2474 'T', 'e', 'x', 't',
2475 '\r','\r','\n','\r',
2476 '\n', 0};
2477 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2478 'S', 'o', 'm', 'e',
2479 'T', 'e', 'x', 't',
2480 ' ','\r', 0};
2481 #define MAX_BUF_LEN 1024
2482 WCHAR buf[MAX_BUF_LEN];
2483 char * p;
2484 int result;
2485 CHARRANGE cr;
2486 EDITSTREAM es;
2487
2488 setText.codepage = 1200; /* no constant for unicode */
2489 getText.codepage = 1200; /* no constant for unicode */
2490 getText.cb = MAX_BUF_LEN;
2491 getText.flags = GT_DEFAULT;
2492 getText.lpDefaultChar = NULL;
2493 getText.lpUsedDefChar = NULL;
2494
2495 setText.flags = 0;
2496 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2497 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2498 ok(lstrcmpW(buf, TestItem1) == 0,
2499 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2500
2501 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2502 convert \r to \r\n on return
2503 */
2504 setText.codepage = 1200; /* no constant for unicode */
2505 getText.codepage = 1200; /* no constant for unicode */
2506 getText.cb = MAX_BUF_LEN;
2507 getText.flags = GT_DEFAULT;
2508 getText.lpDefaultChar = NULL;
2509 getText.lpUsedDefChar = NULL;
2510 setText.flags = 0;
2511 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2512 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2513 ok(lstrcmpW(buf, TestItem2) == 0,
2514 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2515
2516 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2517 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2518 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2519 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2520
2521 /* Baseline test for just-enough buffer space for string */
2522 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2523 getText.codepage = 1200; /* no constant for unicode */
2524 getText.flags = GT_DEFAULT;
2525 getText.lpDefaultChar = NULL;
2526 getText.lpUsedDefChar = NULL;
2527 memset(buf, 0, MAX_BUF_LEN);
2528 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2529 ok(lstrcmpW(buf, TestItem2) == 0,
2530 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2531
2532 /* When there is enough space for one character, but not both, of the CRLF
2533 pair at the end of the string, the CR is not copied at all. That is,
2534 the caller must not see CRLF pairs truncated to CR at the end of the
2535 string.
2536 */
2537 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2538 getText.codepage = 1200; /* no constant for unicode */
2539 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2540 getText.lpDefaultChar = NULL;
2541 getText.lpUsedDefChar = NULL;
2542 memset(buf, 0, MAX_BUF_LEN);
2543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2544 ok(lstrcmpW(buf, TestItem1) == 0,
2545 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2546
2547
2548 /* \r\n pairs get changed into \r */
2549 setText.codepage = 1200; /* no constant for unicode */
2550 getText.codepage = 1200; /* no constant for unicode */
2551 getText.cb = MAX_BUF_LEN;
2552 getText.flags = GT_DEFAULT;
2553 getText.lpDefaultChar = NULL;
2554 getText.lpUsedDefChar = NULL;
2555 setText.flags = 0;
2556 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2557 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2558 ok(lstrcmpW(buf, TestItem3_after) == 0,
2559 "EM_SETTEXTEX did not convert properly\n");
2560
2561 /* \n also gets changed to \r */
2562 setText.codepage = 1200; /* no constant for unicode */
2563 getText.codepage = 1200; /* no constant for unicode */
2564 getText.cb = MAX_BUF_LEN;
2565 getText.flags = GT_DEFAULT;
2566 getText.lpDefaultChar = NULL;
2567 getText.lpUsedDefChar = NULL;
2568 setText.flags = 0;
2569 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2570 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2571 ok(lstrcmpW(buf, TestItem3_after) == 0,
2572 "EM_SETTEXTEX did not convert properly\n");
2573
2574 /* \r\r\n gets changed into single space */
2575 setText.codepage = 1200; /* no constant for unicode */
2576 getText.codepage = 1200; /* no constant for unicode */
2577 getText.cb = MAX_BUF_LEN;
2578 getText.flags = GT_DEFAULT;
2579 getText.lpDefaultChar = NULL;
2580 getText.lpUsedDefChar = NULL;
2581 setText.flags = 0;
2582 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2583 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2584 ok(lstrcmpW(buf, TestItem4_after) == 0,
2585 "EM_SETTEXTEX did not convert properly\n");
2586
2587 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2588 (WPARAM)&setText, (LPARAM) NULL);
2589 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2590
2591 ok (result == 1,
2592 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2593 ok(lstrlenW(buf) == 0,
2594 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2595
2596 /* put some text back */
2597 setText.flags = 0;
2598 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2599 /* select some text */
2600 cr.cpMax = 1;
2601 cr.cpMin = 3;
2602 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2603 /* replace current selection */
2604 setText.flags = ST_SELECTION;
2605 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2606 (WPARAM)&setText, (LPARAM) NULL);
2607 ok(result == 0,
2608 "EM_SETTEXTEX with NULL lParam to replace selection"
2609 " with no text should return 0. Got %i\n",
2610 result);
2611
2612 /* put some text back */
2613 setText.flags = 0;
2614 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2615 /* select some text */
2616 cr.cpMax = 1;
2617 cr.cpMin = 3;
2618 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2619 /* replace current selection */
2620 setText.flags = ST_SELECTION;
2621 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2622 (WPARAM)&setText, (LPARAM) TestItem1);
2623 /* get text */
2624 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2625 ok(result == lstrlenW(TestItem1),
2626 "EM_SETTEXTEX with NULL lParam to replace selection"
2627 " with no text should return 0. Got %i\n",
2628 result);
2629 ok(lstrlenW(buf) == 22,
2630 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2631 lstrlenW(buf) );
2632
2633 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2635 p = (char *)buf;
2636 es.dwCookie = (DWORD_PTR)&p;
2637 es.dwError = 0;
2638 es.pfnCallback = test_WM_SETTEXT_esCallback;
2639 memset(buf, 0, sizeof(buf));
2640 SendMessage(hwndRichEdit, EM_STREAMOUT,
2641 (WPARAM)(SF_RTF), (LPARAM)&es);
2642 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2643
2644 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2645 getText.codepage = 1200; /* no constant for unicode */
2646 getText.cb = MAX_BUF_LEN;
2647 getText.flags = GT_DEFAULT;
2648 getText.lpDefaultChar = NULL;
2649 getText.lpUsedDefChar = NULL;
2650
2651 setText.flags = 0;
2652 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2653 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2654 ok(lstrcmpW(buf, TestItem1) == 0,
2655 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2656
2657
2658 DestroyWindow(hwndRichEdit);
2659 }
2660
2661 static void test_EM_LIMITTEXT(void)
2662 {
2663 int ret;
2664
2665 HWND hwndRichEdit = new_richedit(NULL);
2666
2667 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2668 * about setting the length to -1 for multiline edit controls doesn't happen.
2669 */
2670
2671 /* Don't check default gettextlimit case. That's done in other tests */
2672
2673 /* Set textlimit to 100 */
2674 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2675 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2676 ok (ret == 100,
2677 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2678
2679 /* Set textlimit to 0 */
2680 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2681 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2682 ok (ret == 65536,
2683 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2684
2685 /* Set textlimit to -1 */
2686 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2687 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2688 ok (ret == -1,
2689 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2690
2691 /* Set textlimit to -2 */
2692 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2693 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2694 ok (ret == -2,
2695 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2696
2697 DestroyWindow (hwndRichEdit);
2698 }
2699
2700
2701 static void test_EM_EXLIMITTEXT(void)
2702 {
2703 int i, selBegin, selEnd, len1, len2;
2704 int result;
2705 char text[1024 + 1];
2706 char buffer[1024 + 1];
2707 int textlimit = 0; /* multiple of 100 */
2708 HWND hwndRichEdit = new_richedit(NULL);
2709
2710 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2711 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2712
2713 textlimit = 256000;
2714 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2715 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2716 /* set higher */
2717 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2718
2719 textlimit = 1000;
2720 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2721 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2722 /* set lower */
2723 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2724
2725 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2726 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2727 /* default for WParam = 0 */
2728 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2729
2730 textlimit = sizeof(text)-1;
2731 memset(text, 'W', textlimit);
2732 text[sizeof(text)-1] = 0;
2733 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2734 /* maxed out text */
2735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2736
2737 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2738 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2739 len1 = selEnd - selBegin;
2740
2741 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2742 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2743 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2744 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2745 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2746 len2 = selEnd - selBegin;
2747
2748 ok(len1 != len2,
2749 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2750 len1,len2,i);
2751
2752 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2753 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2754 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2755 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2756 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2757 len1 = selEnd - selBegin;
2758
2759 ok(len1 != len2,
2760 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2761 len1,len2,i);
2762
2763 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2764 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2765 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2766 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2767 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2768 len2 = selEnd - selBegin;
2769
2770 ok(len1 == len2,
2771 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2772 len1,len2,i);
2773
2774 /* set text up to the limit, select all the text, then add a char */
2775 textlimit = 5;
2776 memset(text, 'W', textlimit);
2777 text[textlimit] = 0;
2778 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2780 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2781 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2782 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2783 result = strcmp(buffer, "A");
2784 ok(0 == result, "got string = \"%s\"\n", buffer);
2785
2786 /* WM_SETTEXT not limited */
2787 textlimit = 10;
2788 memset(text, 'W', textlimit);
2789 text[textlimit] = 0;
2790 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2792 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2793 i = strlen(buffer);
2794 ok(10 == i, "expected 10 chars\n");
2795 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2796 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2797
2798 /* try inserting more text at end */
2799 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2800 ok(0 == i, "WM_CHAR wasn't processed\n");
2801 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2802 i = strlen(buffer);
2803 ok(10 == i, "expected 10 chars, got %i\n", i);
2804 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2805 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2806
2807 /* try inserting text at beginning */
2808 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2809 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2810 ok(0 == i, "WM_CHAR wasn't processed\n");
2811 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2812 i = strlen(buffer);
2813 ok(10 == i, "expected 10 chars, got %i\n", i);
2814 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2815 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2816
2817 /* WM_CHAR is limited */
2818 textlimit = 1;
2819 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2820 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2821 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2822 ok(0 == i, "WM_CHAR wasn't processed\n");
2823 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2824 ok(0 == i, "WM_CHAR wasn't processed\n");
2825 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2826 i = strlen(buffer);
2827 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2828
2829 DestroyWindow(hwndRichEdit);
2830 }
2831
2832 static void test_EM_GETLIMITTEXT(void)
2833 {
2834 int i;
2835 HWND hwndRichEdit = new_richedit(NULL);
2836
2837 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2838 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2839
2840 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2841 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2842 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2843
2844 DestroyWindow(hwndRichEdit);
2845 }
2846
2847 static void test_WM_SETFONT(void)
2848 {
2849 /* There is no invalid input or error conditions for this function.
2850 * NULL wParam and lParam just fall back to their default values
2851 * It should be noted that even if you use a gibberish name for your fonts
2852 * here, it will still work because the name is stored. They will display as
2853 * System, but will report their name to be whatever they were created as */
2854
2855 HWND hwndRichEdit = new_richedit(NULL);
2856 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2857 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2858 FF_DONTCARE, "Marlett");
2859 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2860 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2861 FF_DONTCARE, "MS Sans Serif");
2862 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2863 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2864 FF_DONTCARE, "Courier");
2865 LOGFONTA sentLogFont;
2866 CHARFORMAT2A returnedCF2A;
2867
2868 returnedCF2A.cbSize = sizeof(returnedCF2A);
2869
2870 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2871 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2872 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2873
2874 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2875 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2876 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2877 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2878
2879 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2880 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2881 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2882 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2883 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2884 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2885
2886 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2887 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2888 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2889 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2890 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2891 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2892
2893 /* This last test is special since we send in NULL. We clear the variables
2894 * and just compare to "System" instead of the sent in font name. */
2895 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2896 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2897 returnedCF2A.cbSize = sizeof(returnedCF2A);
2898
2899 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2900 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2901 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2902 ok (!strcmp("System",returnedCF2A.szFaceName),
2903 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2904
2905 DestroyWindow(hwndRichEdit);
2906 }
2907
2908
2909 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2910 LPBYTE pbBuff,
2911 LONG cb,
2912 LONG *pcb)
2913 {
2914 const char** str = (const char**)dwCookie;
2915 int size = strlen(*str);
2916 if(size > 3) /* let's make it piecemeal for fun */
2917 size = 3;
2918 *pcb = cb;
2919 if (*pcb > size) {
2920 *pcb = size;
2921 }
2922 if (*pcb > 0) {
2923 memcpy(pbBuff, *str, *pcb);
2924 *str += *pcb;
2925 }
2926 return 0;
2927 }
2928
2929 static void test_EM_GETMODIFY(void)
2930 {
2931 HWND hwndRichEdit = new_richedit(NULL);
2932 LRESULT result;
2933 SETTEXTEX setText;
2934 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2935 'S', 'o', 'm', 'e',
2936 'T', 'e', 'x', 't', 0};
2937 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2938 'S', 'o', 'm', 'e',
2939 'O', 't', 'h', 'e', 'r',
2940 'T', 'e', 'x', 't', 0};
2941 const char* streamText = "hello world";
2942 CHARFORMAT2 cf2;
2943 PARAFORMAT2 pf2;
2944 EDITSTREAM es;
2945
2946 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2947 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2948 FF_DONTCARE, "Courier");
2949
2950 setText.codepage = 1200; /* no constant for unicode */
2951 setText.flags = ST_KEEPUNDO;
2952
2953
2954 /* modify flag shouldn't be set when richedit is first created */
2955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2956 ok (result == 0,
2957 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2958
2959 /* setting modify flag should actually set it */
2960 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2961 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2962 ok (result != 0,
2963 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2964
2965 /* clearing modify flag should actually clear it */
2966 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2967 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2968 ok (result == 0,
2969 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2970
2971 /* setting font doesn't change modify flag */
2972 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2973 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2974 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2975 ok (result == 0,
2976 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2977
2978 /* setting text should set modify flag */
2979 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2980 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2981 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982 ok (result != 0,
2983 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2984
2985 /* undo previous text doesn't reset modify flag */
2986 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2987 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2988 ok (result != 0,
2989 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2990
2991 /* set text with no flag to keep undo stack should not set modify flag */
2992 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2993 setText.flags = 0;
2994 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2995 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2996 ok (result == 0,
2997 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2998
2999 /* WM_SETTEXT doesn't modify */
3000 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3002 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3003 ok (result == 0,
3004 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3005
3006 /* clear the text */
3007 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3008 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3009 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3010 ok (result == 0,
3011 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3012
3013 /* replace text */
3014 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3015 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3016 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3017 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3018 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3019 ok (result != 0,
3020 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3021
3022 /* copy/paste text 1 */
3023 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3024 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3025 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3026 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3027 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3028 ok (result != 0,
3029 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3030
3031 /* copy/paste text 2 */
3032 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3033 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3034 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3035 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3036 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3037 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3038 ok (result != 0,
3039 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3040
3041 /* press char */
3042 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3043 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3044 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3045 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3046 ok (result != 0,
3047 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3048
3049 /* press del */
3050 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3051 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3052 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3053 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3054 ok (result != 0,
3055 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3056
3057 /* set char format */
3058 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3059 cf2.cbSize = sizeof(CHARFORMAT2);
3060 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3061 (LPARAM) &cf2);
3062 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3063 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3064 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3065 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3066 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3067 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3068 ok (result != 0,
3069 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3070
3071 /* set para format */
3072 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3073 pf2.cbSize = sizeof(PARAFORMAT2);
3074 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3075 (LPARAM) &pf2);
3076 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3077 pf2.wAlignment = PFA_RIGHT;
3078 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3079 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3080 ok (result == 0,
3081 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3082
3083 /* EM_STREAM */
3084 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3085 es.dwCookie = (DWORD_PTR)&streamText;
3086 es.dwError = 0;
3087 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3088 SendMessage(hwndRichEdit, EM_STREAMIN,
3089 (WPARAM)(SF_TEXT), (LPARAM)&es);
3090 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3091 ok (result != 0,
3092 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3093
3094 DestroyWindow(hwndRichEdit);
3095 }
3096
3097 struct exsetsel_s {
3098 long min;
3099 long max;
3100 long expected_retval;
3101 int expected_getsel_start;
3102 int expected_getsel_end;
3103 int _exsetsel_todo_wine;
3104 int _getsel_todo_wine;
3105 };
3106
3107 const struct exsetsel_s exsetsel_tests[] = {
3108 /* sanity tests */
3109 {5, 10, 10, 5, 10, 0, 0},
3110 {15, 17, 17, 15, 17, 0, 0},
3111 /* test cpMax > strlen() */
3112 {0, 100, 18, 0, 18, 0, 1},
3113 /* test cpMin == cpMax */
3114 {5, 5, 5, 5, 5, 0, 0},
3115 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3116 {-1, 0, 5, 5, 5, 0, 0},
3117 {-1, 17, 5, 5, 5, 0, 0},
3118 {-1, 18, 5, 5, 5, 0, 0},
3119 /* test cpMin < 0 && cpMax < 0 */
3120 {-1, -1, 17, 17, 17, 0, 0},
3121 {-4, -5, 17, 17, 17, 0, 0},
3122 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3123 {0, -1, 18, 0, 18, 0, 1},
3124 {17, -5, 18, 17, 18, 0, 1},
3125 {18, -3, 17, 17, 17, 0, 0},
3126 /* test if cpMin > cpMax */
3127 {15, 19, 18, 15, 18, 0, 1},
3128 {19, 15, 18, 15, 18, 0, 1}
3129 };
3130
3131 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3132 CHARRANGE cr;
3133 long result;
3134 int start, end;
3135
3136 cr.cpMin = setsel->min;
3137 cr.cpMax = setsel->max;
3138 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3139
3140 if (setsel->_exsetsel_todo_wine) {
3141 todo_wine {
3142 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3143 }
3144 } else {
3145 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3146 }
3147
3148 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3149
3150 if (setsel->_getsel_todo_wine) {
3151 todo_wine {
3152 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
3153 }
3154 } else {
3155 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
3156 }
3157 }
3158
3159 static void test_EM_EXSETSEL(void)
3160 {
3161 HWND hwndRichEdit = new_richedit(NULL);
3162 int i;
3163 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3164
3165 /* sending some text to the window */
3166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3167 /* 01234567890123456*/
3168 /* 10 */
3169
3170 for (i = 0; i < num_tests; i++) {
3171 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3172 }
3173
3174 DestroyWindow(hwndRichEdit);
3175 }
3176
3177 static void test_EM_REPLACESEL(int redraw)
3178 {
3179 HWND hwndRichEdit = new_richedit(NULL);
3180 char buffer[1024] = {0};
3181 int r;
3182 GETTEXTEX getText;
3183 CHARRANGE cr;
3184
3185 /* sending some text to the window */
3186 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3187 /* 01234567890123456*/
3188 /* 10 */
3189
3190 /* FIXME add more tests */
3191 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3192 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3193 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3194 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3195 r = strcmp(buffer, "testing");
3196 ok(0 == r, "expected %d, got %d\n", 0, r);
3197
3198 DestroyWindow(hwndRichEdit);
3199
3200 hwndRichEdit = new_richedit(NULL);
3201
3202 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3203 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3204
3205 /* Test behavior with carriage returns and newlines */
3206 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3207 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3208 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3209 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3210 r = strcmp(buffer, "RichEdit1");
3211 ok(0 == r, "expected %d, got %d\n", 0, r);
3212 getText.cb = 1024;
3213 getText.codepage = CP_ACP;
3214 getText.flags = GT_DEFAULT;
3215 getText.lpDefaultChar = NULL;
3216 getText.lpUsedDefChar = NULL;
3217 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3218 ok(strcmp(buffer, "RichEdit1") == 0,
3219 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3220
3221 /* Test number of lines reported after EM_REPLACESEL */
3222 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3223 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3224
3225 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3226 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3227 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3228 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3229 r = strcmp(buffer, "RichEdit1\r\n");
3230 ok(0 == r, "expected %d, got %d\n", 0, r);
3231 getText.cb = 1024;
3232 getText.codepage = CP_ACP;
3233 getText.flags = GT_DEFAULT;
3234 getText.lpDefaultChar = NULL;
3235 getText.lpUsedDefChar = NULL;
3236 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3237 ok(strcmp(buffer, "RichEdit1\r") == 0,
3238 "EM_GETTEXTEX returned incorrect string\n");
3239
3240 /* Test number of lines reported after EM_REPLACESEL */
3241 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3242 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3243
3244 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3245 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3246 returns the number of characters *inserted* into the control (after
3247 required conversions), but WinXP's riched20 returns the number of
3248 characters interpreted from the original lParam. Wine's builtin riched20
3249 implements the WinXP behavior.
3250 */
3251 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3252 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3253 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3254 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3255
3256 /* Test number of lines reported after EM_REPLACESEL */
3257 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3258 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3259
3260 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3261 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3262 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3263 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3264
3265 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3266 r = strcmp(buffer, "RichEdit1\r\n");
3267 ok(0 == r, "expected %d, got %d\n", 0, r);
3268 getText.cb = 1024;
3269 getText.codepage = CP_ACP;
3270 getText.flags = GT_DEFAULT;
3271 getText.lpDefaultChar = NULL;
3272 getText.lpUsedDefChar = NULL;
3273 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3274 ok(strcmp(buffer, "RichEdit1\r") == 0,
3275 "EM_GETTEXTEX returned incorrect string\n");
3276
3277 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3278 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3279 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3280 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3281
3282 /* The following tests show that richedit should handle the special \r\r\n
3283 sequence by turning it into a single space on insertion. However,
3284 EM_REPLACESEL on WinXP returns the number of characters in the original
3285 string.
3286 */
3287
3288 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3289 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3290 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3291 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3292 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3293 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3294 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3295
3296 /* Test the actual string */
3297 getText.cb = 1024;
3298 getText.codepage = CP_ACP;
3299 getText.flags = GT_DEFAULT;
3300 getText.lpDefaultChar = NULL;
3301 getText.lpUsedDefChar = NULL;
3302 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3303 ok(strcmp(buffer, "\r\r") == 0,
3304 "EM_GETTEXTEX returned incorrect string\n");
3305
3306 /* Test number of lines reported after EM_REPLACESEL */
3307 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3308 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3309
3310 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3311 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3312 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3313 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3314 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3315 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3316 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3317 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3318
3319 /* Test the actual string */
3320 getText.cb = 1024;
3321 getText.codepage = CP_ACP;
3322 getText.flags = GT_DEFAULT;
3323 getText.lpDefaultChar = NULL;
3324 getText.lpUsedDefChar = NULL;
3325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3326 ok(strcmp(buffer, " ") == 0,
3327 "EM_GETTEXTEX returned incorrect string\n");
3328
3329 /* Test number of lines reported after EM_REPLACESEL */
3330 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3331 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3332
3333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3334 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3335 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3336 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3337 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3338 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3339 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3340 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3341
3342 /* Test the actual string */
3343 getText.cb = 1024;
3344 getText.codepage = CP_ACP;
3345 getText.flags = GT_DEFAULT;
3346 getText.lpDefaultChar = NULL;
3347 getText.lpUsedDefChar = NULL;
3348 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3349 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3350 "EM_GETTEXTEX returned incorrect string\n");
3351
3352 /* Test number of lines reported after EM_REPLACESEL */
3353 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3354 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3355
3356 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3357 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3358 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3359 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3360 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3361 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3362 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3363 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3364
3365 /* Test the actual string */
3366 getText.cb = 1024;
3367 getText.codepage = CP_ACP;
3368 getText.flags = GT_DEFAULT;
3369 getText.lpDefaultChar = NULL;
3370 getText.lpUsedDefChar = NULL;
3371 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3372 ok(strcmp(buffer, " \r") == 0,
3373 "EM_GETTEXTEX returned incorrect string\n");
3374
3375 /* Test number of lines reported after EM_REPLACESEL */
3376 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3377 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3378
3379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3380 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3381 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3382 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3383 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3384 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3385 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3386 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3387
3388 /* Test the actual string */
3389 getText.cb = 1024;
3390 getText.codepage = CP_ACP;
3391 getText.flags = GT_DEFAULT;
3392 getText.lpDefaultChar = NULL;
3393 getText.lpUsedDefChar = NULL;
3394 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3395 ok(strcmp(buffer, " \r\r") == 0,
3396 "EM_GETTEXTEX returned incorrect string\n");
3397
3398 /* Test number of lines reported after EM_REPLACESEL */
3399 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3400 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3401
3402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3403 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3404 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3405 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3406 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3407 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3408 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3409 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3410
3411 /* Test the actual string */
3412 getText.cb = 1024;
3413 getText.codepage = CP_ACP;
3414 getText.flags = GT_DEFAULT;
3415 getText.lpDefaultChar = NULL;
3416 getText.lpUsedDefChar = NULL;
3417 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3418 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3419 "EM_GETTEXTEX returned incorrect string\n");
3420
3421 /* Test number of lines reported after EM_REPLACESEL */
3422 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3423 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3424
3425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3426 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3427 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3428 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3429 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3430 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3431 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3432
3433 /* Test the actual string */
3434 getText.cb = 1024;
3435 getText.codepage = CP_ACP;
3436 getText.flags = GT_DEFAULT;
3437 getText.lpDefaultChar = NULL;
3438 getText.lpUsedDefChar = NULL;
3439 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3440 ok(strcmp(buffer, "\r\r") == 0,
3441 "EM_GETTEXTEX returned incorrect string\n");
3442
3443 /* Test number of lines reported after EM_REPLACESEL */
3444 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3445 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3446
3447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3448 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3449 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3450 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3451 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3452 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3453 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3454 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3455
3456 /* Test the actual string */
3457 getText.cb = 1024;
3458 getText.codepage = CP_ACP;
3459 getText.flags = GT_DEFAULT;
3460 getText.lpDefaultChar = NULL;
3461 getText.lpUsedDefChar = NULL;
3462 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3463 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3464 "EM_GETTEXTEX returned incorrect string\n");
3465
3466 /* Test number of lines reported after EM_REPLACESEL */
3467 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3468 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3469
3470 if (!redraw)
3471 /* This is needed to avoid interferring with keybd_event calls
3472 * on other tests that simulate keyboard events. */
3473 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
3474
3475 DestroyWindow(hwndRichEdit);
3476 }
3477
3478 static void test_WM_PASTE(void)
3479 {
3480 int result;
3481 char buffer[1024] = {0};
3482 const char* text1 = "testing paste\r";
3483 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3484 const char* text1_after = "testing paste\r\n";
3485 const char* text2 = "testing paste\r\rtesting paste";
3486 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3487 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3488 HWND hwndRichEdit = new_richedit(NULL);
3489
3490 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3491 messages, probably because it inspects the keyboard state itself.
3492 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3493 */
3494
3495 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
3496 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
3497 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
3498 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
3499 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
3500
3501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3502 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3503
3504 SEND_CTRL_C(hwndRichEdit); /* Copy */
3505 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3506 SEND_CTRL_V(hwndRichEdit); /* Paste */
3507 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3508 /* Pasted text should be visible at this step */
3509 result = strcmp(text1_step1, buffer);
3510 ok(result == 0,
3511 "test paste: strcmp = %i\n", result);
3512 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3513 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3514 /* Text should be the same as before (except for \r -> \r\n conversion) */
3515 result = strcmp(text1_after, buffer);
3516 ok(result == 0,
3517 "test paste: strcmp = %i\n", result);
3518
3519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3520 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3521 SEND_CTRL_C(hwndRichEdit); /* Copy */
3522 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3523 SEND_CTRL_V(hwndRichEdit); /* Paste */
3524 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3525 /* Pasted text should be visible at this step */
3526 result = strcmp(text3, buffer);
3527 ok(result == 0,
3528 "test paste: strcmp = %i\n", result);
3529 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3530 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3531 /* Text should be the same as before (except for \r -> \r\n conversion) */
3532 result = strcmp(text2_after, buffer);
3533 ok(result == 0,
3534 "test paste: strcmp = %i\n", result);
3535 SEND_CTRL_Y(hwndRichEdit); /* Redo */
3536 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3537 /* Text should revert to post-paste state */
3538 result = strcmp(buffer,text3);
3539 ok(result == 0,
3540 "test paste: strcmp = %i\n", result);
3541
3542 DestroyWindow(hwndRichEdit);
3543 }
3544
3545 static void test_EM_FORMATRANGE(void)
3546 {
3547 int r;
3548 FORMATRANGE fr;
3549 HDC hdc;
3550 HWND hwndRichEdit = new_richedit(NULL);
3551
3552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3553
3554 hdc = GetDC(hwndRichEdit);
3555 ok(hdc != NULL, "Could not get HDC\n");
3556
3557 fr.hdc = fr.hdcTarget = hdc;
3558 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3559 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3560 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3561 fr.chrg.cpMin = 0;
3562 fr.chrg.cpMax = 20;
3563
3564 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3565 todo_wine {
3566 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3567 }
3568
3569 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3570 todo_wine {
3571 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3572 }
3573
3574 fr.chrg.cpMin = 0;
3575 fr.chrg.cpMax = 10;
3576
3577 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3578 todo_wine {
3579 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3580 }
3581
3582 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3583 todo_wine {
3584 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3585 }
3586
3587 DestroyWindow(hwndRichEdit);
3588 }
3589
3590 static int nCallbackCount = 0;
3591
3592 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3593 LONG cb, LONG* pcb)
3594 {
3595 const char text[] = {'t','e','s','t'};
3596
3597 if (sizeof(text) <= cb)
3598 {
3599 if ((int)dwCookie != nCallbackCount)
3600 {
3601 *pcb = 0;
3602 return 0;
3603 }
3604
3605 memcpy (pbBuff, text, sizeof(text));
3606 *pcb = sizeof(text);
3607
3608 nCallbackCount++;
3609
3610 return 0;
3611 }
3612 else
3613 return 1; /* indicates callback failed */
3614 }
3615
3616 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3617 LPBYTE pbBuff,
3618 LONG cb,
3619 LONG *pcb)
3620 {
3621 const char** str = (const char**)dwCookie;
3622 int size = strlen(*str);
3623 *pcb = cb;
3624 if (*pcb > size) {
3625 *pcb = size;
3626 }
3627 if (*pcb > 0) {
3628 memcpy(pbBuff, *str, *pcb);
3629 *str += *pcb;
3630 }
3631 return 0;
3632 }
3633
3634 struct StringWithLength {
3635 int length;
3636 char *buffer;
3637 };
3638
3639 /* This callback is used to handled the null characters in a string. */
3640 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
3641 LPBYTE pbBuff,
3642 LONG cb,
3643 LONG *pcb)
3644 {
3645 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
3646 int size = str->length;
3647 *pcb = cb;
3648 if (*pcb > size) {
3649 *pcb = size;
3650 }
3651 if (*pcb > 0) {
3652 memcpy(pbBuff, str->buffer, *pcb);
3653 str->buffer += *pcb;
3654 str->length -= *pcb;
3655 }
3656 return 0;
3657 }
3658
3659 static void test_EM_STREAMIN(void)
3660 {
3661 HWND hwndRichEdit = new_richedit(NULL);
3662 LRESULT result;
3663 EDITSTREAM es;
3664 char buffer[1024] = {0};
3665
3666 const char * streamText0 = "{\\rtf1 TestSomeText}";
3667 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3668 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3669
3670 const char * streamText1 =
3671 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3672 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3673 "}\r\n";
3674
3675 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3676 const char * streamText2 =
3677 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3678 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3679 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3680 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3681 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3682 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3683 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3684
3685 const char * streamText3 = "RichEdit1";
3686
3687 struct StringWithLength cookieForStream4;
3688 const char * streamText4 =
3689 "This text just needs to be long enough to cause run to be split onto "\
3690 "two seperate lines and make sure the null terminating character is "\
3691 "handled properly.\0";
3692 int length4 = strlen(streamText4) + 1;
3693 cookieForStream4.buffer = (char *)streamText4;
3694 cookieForStream4.length = length4;
3695
3696 /* Minimal test without \par at the end */
3697 es.dwCookie = (DWORD_PTR)&streamText0;
3698 es.dwError = 0;
3699 es.pfnCallback = test_EM_STREAMIN_esCallback;
3700 SendMessage(hwndRichEdit, EM_STREAMIN,
3701 (WPARAM)(SF_RTF), (LPARAM)&es);
3702
3703 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3704 ok (result == 12,
3705 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3706 result = strcmp (buffer,"TestSomeText");
3707 ok (result == 0,
3708 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3709 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3710
3711 /* Native richedit 2.0 ignores last \par */
3712 es.dwCookie = (DWORD_PTR)&streamText0a;
3713 es.dwError = 0;
3714 es.pfnCallback = test_EM_STREAMIN_esCallback;
3715 SendMessage(hwndRichEdit, EM_STREAMIN,
3716 (WPARAM)(SF_RTF), (LPARAM)&es);
3717
3718 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3719 ok (result == 12,
3720 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3721 result = strcmp (buffer,"TestSomeText");
3722 ok (result == 0,
3723 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3724 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3725
3726 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3727 es.dwCookie = (DWORD_PTR)&streamText0b;
3728 es.dwError = 0;
3729 es.pfnCallback = test_EM_STREAMIN_esCallback;
3730 SendMessage(hwndRichEdit, EM_STREAMIN,
3731 (WPARAM)(SF_RTF), (LPARAM)&es);
3732
3733 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3734 ok (result == 14,
3735 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3736 result = strcmp (buffer,"TestSomeText\r\n");
3737 ok (result == 0,
3738 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3739 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3740
3741 es.dwCookie = (DWORD_PTR)&streamText1;
3742 es.dwError = 0;
3743 es.pfnCallback = test_EM_STREAMIN_esCallback;
3744 SendMessage(hwndRichEdit, EM_STREAMIN,
3745 (WPARAM)(SF_RTF), (LPARAM)&es);
3746
3747 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3748 ok (result == 12,
3749 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3750 result = strcmp (buffer,"TestSomeText");
3751 ok (result == 0,
3752 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3753 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3754
3755 es.dwCookie = (DWORD_PTR)&streamText2;
3756 es.dwError = 0;
3757 SendMessage(hwndRichEdit, EM_STREAMIN,
3758 (WPARAM)(SF_RTF), (LPARAM)&es);
3759
3760 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3761 ok (result == 0,
3762 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3763 ok (strlen(buffer) == 0,
3764 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3765 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3766
3767 es.dwCookie = (DWORD_PTR)&streamText3;
3768 es.dwError = 0;
3769 SendMessage(hwndRichEdit, EM_STREAMIN,
3770 (WPARAM)(SF_RTF), (LPARAM)&es);
3771
3772 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3773 ok (result == 0,
3774 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3775 ok (strlen(buffer) == 0,
3776 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3777 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3778
3779 es.dwCookie = (DWORD_PTR)&cookieForStream4;
3780 es.dwError = 0;
3781 es.pfnCallback = test_EM_STREAMIN_esCallback2;
3782 SendMessage(hwndRichEdit, EM_STREAMIN,
3783 (WPARAM)(SF_TEXT), (LPARAM)&es);
3784
3785 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3786 ok (result == length4,
3787 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
3788 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
3789
3790 DestroyWindow(hwndRichEdit);
3791 }
3792
3793 static void test_EM_StreamIn_Undo(void)
3794 {
3795 /* The purpose of this test is to determine when a EM_StreamIn should be
3796 * undoable. This is important because WM_PASTE currently uses StreamIn and
3797 * pasting should always be undoable but streaming isn't always.
3798 *
3799 * cases to test:
3800 * StreamIn plain text without SFF_SELECTION.
3801 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3802 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3803 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3804 * Feel free to add tests for other text modes or StreamIn things.
3805 */
3806
3807
3808 HWND hwndRichEdit = new_richedit(NULL);
3809 LRESULT result;
3810 EDITSTREAM es;
3811 char buffer[1024] = {0};
3812 const char randomtext[] = "Some text";
3813
3814 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3815
3816 /* StreamIn, no SFF_SELECTION */
3817 es.dwCookie = nCallbackCount;
3818 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3819 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3820 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3821 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3822 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3823 result = strcmp (buffer,"test");
3824 ok (result == 0,
3825 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3826
3827 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3828 ok (result == FALSE,
3829 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3830
3831 /* StreamIn, SFF_SELECTION, but nothing selected */
3832 es.dwCookie = nCallbackCount;
3833 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3834 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3835 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3836 SendMessage(hwndRichEdit, EM_STREAMIN,
3837 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3838 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3839 result = strcmp (buffer,"testSome text");
3840 ok (result == 0,
3841 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3842
3843 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3844 ok (result == TRUE,
3845 "EM_STREAMIN with SFF_SELECTION but no selection set "
3846 "should create an undo\n");
3847
3848 /* StreamIn, SFF_SELECTION, with a selection */
3849 es.dwCookie = nCallbackCount;
3850 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3851 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3852 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3853 SendMessage(hwndRichEdit, EM_STREAMIN,
3854 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3855 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3856 result = strcmp (buffer,"Sometesttext");
3857 ok (result == 0,
3858 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3859
3860 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3861 ok (result == TRUE,
3862 "EM_STREAMIN with SFF_SELECTION and selection set "
3863 "should create an undo\n");
3864
3865 DestroyWindow(hwndRichEdit);
3866 }
3867
3868 static BOOL is_em_settextex_supported(HWND hwnd)
3869 {
3870 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3871 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3872 }
3873
3874 static void test_unicode_conversions(void)
3875 {
3876 static const WCHAR tW[] = {'t',0};
3877 static const WCHAR teW[] = {'t','e',0};
3878 static const WCHAR textW[] = {'t','e','s','t',0};
3879 static const char textA[] = "test";
3880 char bufA[64];
3881 WCHAR bufW[64];
3882 HWND hwnd;
3883 int is_win9x, em_settextex_supported, ret;
3884
3885 is_win9x = GetVersion() & 0x80000000;
3886
3887 #define set_textA(hwnd, wm_set_text, txt) \
3888 do { \
3889 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3890 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3891 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3892 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3893 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3894 } while(0)
3895 #define expect_textA(hwnd, wm_get_text, txt) \
3896 do { \
3897 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3898 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3899 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3900 memset(bufA, 0xAA, sizeof(bufA)); \
3901 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3902 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3903 ret = lstrcmpA(bufA, txt); \
3904 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3905 } while(0)
3906
3907 #define set_textW(hwnd, wm_set_text, txt) \
3908 do { \
3909 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3910 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3911 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3912 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3913 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3914 } while(0)
3915 #define expect_textW(hwnd, wm_get_text, txt) \
3916 do { \
3917 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3918 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3919 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3920 memset(bufW, 0xAA, sizeof(bufW)); \
3921 if (is_win9x) \
3922 { \
3923 assert(wm_get_text == EM_GETTEXTEX); \
3924 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3925 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3926 } \
3927 else \
3928 { \
3929 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3930 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3931 } \
3932 ret = lstrcmpW(bufW, txt); \
3933 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3934 } while(0)
3935 #define expect_empty(hwnd, wm_get_text) \
3936 do { \
3937 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3938 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3939 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3940 memset(bufA, 0xAA, sizeof(bufA)); \
3941 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3942 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3943 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3944 } while(0)
3945
3946 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3947 0, 0, 200, 60, 0, 0, 0, 0);
3948 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3949
3950 ret = IsWindowUnicode(hwnd);
3951 if (is_win9x)
3952 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3953 else
3954 ok(ret, "RichEdit20W should be unicode under NT\n");
3955
3956 /* EM_SETTEXTEX is supported starting from version 3.0 */
3957 em_settextex_supported = is_em_settextex_supported(hwnd);
3958 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3959 em_settextex_supported ? "" : "NOT ");
3960
3961 expect_empty(hwnd, WM_GETTEXT);
3962 expect_empty(hwnd, EM_GETTEXTEX);
3963
3964 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3965 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3966 expect_textA(hwnd, WM_GETTEXT, "t");
3967 expect_textA(hwnd, EM_GETTEXTEX, "t");
3968 expect_textW(hwnd, EM_GETTEXTEX, tW);
3969
3970 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3971 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3972 expect_textA(hwnd, WM_GETTEXT, "te");
3973 expect_textA(hwnd, EM_GETTEXTEX, "te");
3974 expect_textW(hwnd, EM_GETTEXTEX, teW);
3975
3976 set_textA(hwnd, WM_SETTEXT, NULL);
3977 expect_empty(hwnd, WM_GETTEXT);
3978 expect_empty(hwnd, EM_GETTEXTEX);
3979
3980 if (is_win9x)
3981 set_textA(hwnd, WM_SETTEXT, textW);
3982 else
3983 set_textA(hwnd, WM_SETTEXT, textA);
3984 expect_textA(hwnd, WM_GETTEXT, textA);
3985 expect_textA(hwnd, EM_GETTEXTEX, textA);
3986 expect_textW(hwnd, EM_GETTEXTEX, textW);
3987
3988 if (em_settextex_supported)
3989 {
3990 set_textA(hwnd, EM_SETTEXTEX, textA);
3991 expect_textA(hwnd, WM_GETTEXT, textA);
3992 expect_textA(hwnd, EM_GETTEXTEX, textA);
3993 expect_textW(hwnd, EM_GETTEXTEX, textW);
3994 }
3995
3996 if (!is_win9x)
3997 {
3998 set_textW(hwnd, WM_SETTEXT, textW);
3999 expect_textW(hwnd, WM_GETTEXT, textW);
4000 expect_textA(hwnd, WM_GETTEXT, textA);
4001 expect_textW(hwnd, EM_GETTEXTEX, textW);
4002 expect_textA(hwnd, EM_GETTEXTEX, textA);
4003
4004 if (em_settextex_supported)
4005 {
4006 set_textW(hwnd, EM_SETTEXTEX, textW);
4007 expect_textW(hwnd, WM_GETTEXT, textW);
4008 expect_textA(hwnd, WM_GETTEXT, textA);
4009 expect_textW(hwnd, EM_GETTEXTEX, textW);
4010 expect_textA(hwnd, EM_GETTEXTEX, textA);
4011 }
4012 }
4013 DestroyWindow(hwnd);
4014
4015 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4016 0, 0, 200, 60, 0, 0, 0, 0);
4017 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4018
4019 ret = IsWindowUnicode(hwnd);
4020 ok(!ret, "RichEdit20A should NOT be unicode\n");
4021
4022 set_textA(hwnd, WM_SETTEXT, textA);
4023 expect_textA(hwnd, WM_GETTEXT, textA);
4024 expect_textA(hwnd, EM_GETTEXTEX, textA);
4025 expect_textW(hwnd, EM_GETTEXTEX, textW);
4026
4027 if (em_settextex_supported)
4028 {
4029 set_textA(hwnd, EM_SETTEXTEX, textA);
4030 expect_textA(hwnd, WM_GETTEXT, textA);
4031 expect_textA(hwnd, EM_GETTEXTEX, textA);
4032 expect_textW(hwnd, EM_GETTEXTEX, textW);
4033 }
4034
4035 if (!is_win9x)
4036 {
4037 set_textW(hwnd, WM_SETTEXT, textW);
4038 expect_textW(hwnd, WM_GETTEXT, textW);
4039 expect_textA(hwnd, WM_GETTEXT, textA);
4040 expect_textW(hwnd, EM_GETTEXTEX, textW);
4041 expect_textA(hwnd, EM_GETTEXTEX, textA);
4042
4043 if (em_settextex_supported)
4044 {
4045 set_textW(hwnd, EM_SETTEXTEX, textW);
4046 expect_textW(hwnd, WM_GETTEXT, textW);
4047 expect_textA(hwnd, WM_GETTEXT, textA);
4048 expect_textW(hwnd, EM_GETTEXTEX, textW);
4049 expect_textA(hwnd, EM_GETTEXTEX, textA);
4050 }
4051 }
4052 DestroyWindow(hwnd);
4053 }
4054
4055 static void test_WM_CHAR(void)
4056 {
4057 HWND hwnd;
4058 int ret;
4059 const char * char_list = "abc\rabc\r";
4060 const char * expected_content_single = "abcabc";
4061 const char * expected_content_multi = "abc\r\nabc\r\n";
4062 char buffer[64] = {0};
4063 const char * p;
4064
4065 /* single-line control must IGNORE carriage returns */
4066 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4067 0, 0, 200, 60, 0, 0, 0, 0);
4068 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4069
4070 p = char_list;
4071 while (*p != '\0') {
4072 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4073 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4074 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4075 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4076 p++;
4077 }
4078
4079 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4080 ret = strcmp(buffer, expected_content_single);
4081 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4082
4083 DestroyWindow(hwnd);
4084
4085 /* multi-line control inserts CR normally */
4086 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4087 0, 0, 200, 60, 0, 0, 0, 0);
4088 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4089
4090 p = char_list;
4091 while (*p != '\0') {
4092 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4093 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4094 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4095 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4096 p++;
4097 }
4098
4099 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4100 ret = strcmp(buffer, expected_content_multi);
4101 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4102
4103 DestroyWindow(hwnd);
4104 }
4105
4106 static void test_EM_GETTEXTLENGTHEX(void)
4107 {
4108 HWND hwnd;
4109 GETTEXTLENGTHEX gtl;
4110 int ret;
4111 const char * base_string = "base string";
4112 const char * test_string = "a\nb\n\n\r\n";
4113 const char * test_string_after = "a";
4114 const char * test_string_2 = "a\rtest\rstring";
4115 char buffer[64] = {0};
4116
4117 /* single line */
4118 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4119 0, 0, 200, 60, 0, 0, 0, 0);
4120 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4121
4122 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4123 gtl.codepage = CP_ACP;
4124 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4125 ok(ret == 0, "ret %d\n",ret);
4126
4127 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4128 gtl.codepage = CP_ACP;
4129 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4130 ok(ret == 0, "ret %d\n",ret);
4131
4132 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4133
4134 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4135 gtl.codepage = CP_ACP;
4136 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4137 ok(ret == strlen(base_string), "ret %d\n",ret);
4138
4139 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4140 gtl.codepage = CP_ACP;
4141 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4142 ok(ret == strlen(base_string), "ret %d\n",ret);
4143
4144 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4145
4146 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4147 gtl.codepage = CP_ACP;
4148 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4149 ok(ret == 1, "ret %d\n",ret);
4150
4151 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4152 gtl.codepage = CP_ACP;
4153 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4154 ok(ret == 1, "ret %d\n",ret);
4155
4156 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4157 ret = strcmp(buffer, test_string_after);
4158 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4159
4160 DestroyWindow(hwnd);
4161
4162 /* multi line */
4163 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4164 0, 0, 200, 60, 0, 0, 0, 0);
4165 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4166
4167 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4168 gtl.codepage = CP_ACP;
4169 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4170 ok(ret == 0, "ret %d\n",ret);
4171
4172 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4173 gtl.codepage = CP_ACP;
4174 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4175 ok(ret == 0, "ret %d\n",ret);
4176
4177 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4178
4179 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4180 gtl.codepage = CP_ACP;
4181 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4182 ok(ret == strlen(base_string), "ret %d\n",ret);
4183
4184 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4185 gtl.codepage = CP_ACP;
4186 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4187 ok(ret == strlen(base_string), "ret %d\n",ret);
4188
4189 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4190
4191 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4192 gtl.codepage = CP_ACP;
4193 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4194 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4195
4196 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4197 gtl.codepage = CP_ACP;
4198 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4199 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4200
4201 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4202
4203 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4204 gtl.codepage = CP_ACP;
4205 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4206 ok(ret == 10, "ret %d\n",ret);
4207
4208 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4209 gtl.codepage = CP_ACP;
4210 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4211 ok(ret == 6, "ret %d\n",ret);
4212
4213 DestroyWindow(hwnd);
4214 }
4215
4216
4217 /* globals that parent and child access when checking event masks & notifications */
4218 static HWND eventMaskEditHwnd = 0;
4219 static int queriedEventMask;
4220 static int watchForEventMask = 0;
4221
4222 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4223 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4224 {
4225 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4226 {
4227 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4228 }
4229 return DefWindowProcA(hwnd, message, wParam, lParam);
4230 }
4231
4232 /* test event masks in combination with WM_COMMAND */
4233 static void test_eventMask(void)
4234 {
4235 HWND parent;
4236 int ret;
4237 WNDCLASSA cls;
4238 const char text[] = "foo bar\n";
4239 int eventMask;
4240
4241 /* register class to capture WM_COMMAND */
4242 cls.style = 0;
4243 cls.lpfnWndProc = ParentMsgCheckProcA;
4244 cls.cbClsExtra = 0;
4245 cls.cbWndExtra = 0;
4246 cls.hInstance = GetModuleHandleA(0);
4247 cls.hIcon = 0;
4248 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4249 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4250 cls.lpszMenuName = NULL;
4251 cls.lpszClassName = "EventMaskParentClass";
4252 if(!RegisterClassA(&cls)) assert(0);
4253
4254 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4255 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4256 ok (parent != 0, "Failed to create parent window\n");
4257
4258 eventMaskEditHwnd = new_richedit(parent);
4259 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4260
4261 eventMask = ENM_CHANGE | ENM_UPDATE;
4262 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4263 ok(ret == ENM_NONE, "wrong event mask\n");
4264 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4265 ok(ret == eventMask, "failed to set event mask\n");
4266
4267 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4268 queriedEventMask = 0; /* initialize to something other than we expect */
4269 watchForEventMask = EN_CHANGE;
4270 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4271 ok(ret == TRUE, "failed to set text\n");
4272 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4273 notification in response to WM_SETTEXT */
4274 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4275 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4276
4277 }
4278
4279 static int received_WM_NOTIFY = 0;
4280 static int modify_at_WM_NOTIFY = 0;
4281 static HWND hwndRichedit_WM_NOTIFY;
4282
4283 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4284 {
4285 if(message == WM_NOTIFY)
4286 {
4287 received_WM_NOTIFY = 1;
4288 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4289 }
4290 return DefWindowProcA(hwnd, message, wParam, lParam);
4291 }
4292
4293 static void test_WM_NOTIFY(void)
4294 {
4295 HWND parent;
4296 WNDCLASSA cls;
4297 CHARFORMAT2 cf2;
4298
4299 /* register class to capture WM_NOTIFY */
4300 cls.style = 0;
4301 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4302 cls.cbClsExtra = 0;
4303 cls.cbWndExtra = 0;
4304 cls.hInstance = GetModuleHandleA(0);
4305 cls.hIcon = 0;
4306 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4307 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4308 cls.lpszMenuName = NULL;
4309 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4310 if(!RegisterClassA(&cls)) assert(0);
4311
4312 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4313 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4314 ok (parent != 0, "Failed to create parent window\n");
4315
4316 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4317 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4318
4319 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4320
4321 /* Notifications for selection change should only be sent when selection
4322 actually changes. EM_SETCHARFORMAT is one message that calls
4323 ME_CommitUndo, which should check whether message should be sent */
4324 received_WM_NOTIFY = 0;
4325 cf2.cbSize = sizeof(CHARFORMAT2);
4326 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4327 (LPARAM) &cf2);
4328 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4329 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4330 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4331 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4332
4333 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4334 already at 0. */
4335 received_WM_NOTIFY = 0;
4336 modify_at_WM_NOTIFY = 0;
4337 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4338 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4339 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4340
4341 received_WM_NOTIFY = 0;
4342 modify_at_WM_NOTIFY = 0;
4343 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4344 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4345
4346 received_WM_NOTIFY = 0;
4347 modify_at_WM_NOTIFY = 0;
4348 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4349 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4350 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4351
4352 DestroyWindow(hwndRichedit_WM_NOTIFY);
4353 DestroyWindow(parent);
4354 }
4355
4356 static void test_undo_coalescing(void)
4357 {
4358 HWND hwnd;
4359 int result;
4360 char buffer[64] = {0};
4361
4362 /* multi-line control inserts CR normally */
4363 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4364 0, 0, 200, 60, 0, 0, 0, 0);
4365 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4366
4367 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4368 ok (result == FALSE, "Can undo after window creation.\n");
4369 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4370 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
4371 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4372 ok (result == FALSE, "Can redo after window creation.\n");
4373 result = SendMessage(hwnd, EM_REDO, 0, 0);
4374 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
4375
4376 /* Test the effect of arrows keys during typing on undo transactions*/
4377 simulate_typing_characters(hwnd, "one two three");
4378 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
4379 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
4380 simulate_typing_characters(hwnd, " four five six");
4381
4382 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4383 ok (result == FALSE, "Can redo before anything is undone.\n");
4384 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4385 ok (result == TRUE, "Cannot undo typed characters.\n");
4386 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4387 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
4388 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4389 ok (result == TRUE, "Cannot redo after undo.\n");
4390 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4391 result = strcmp(buffer, "one two three");
4392 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4393
4394 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4395 ok (result == TRUE, "Cannot undo typed characters.\n");
4396 result = SendMessage(hwnd, WM_UNDO, 0, 0);
4397 ok (result == TRUE, "Failed to undo typed characters.\n");
4398 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4399 result = strcmp(buffer, "");
4400 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4401
4402 /* Test the effect of focus changes during typing on undo transactions*/
4403 simulate_typing_characters(hwnd, "one two three");
4404 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4405 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4406 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
4407 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
4408 simulate_typing_characters(hwnd, " four five six");
4409 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4410 ok (result == TRUE, "Failed to undo typed characters.\n");
4411 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4412 result = strcmp(buffer, "one two three");
4413 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4414
4415 /* Test the effect of the back key during typing on undo transactions */
4416 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4417 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4418 ok (result == TRUE, "Failed to clear the text.\n");
4419 simulate_typing_characters(hwnd, "one two threa");
4420 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4421 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4422 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
4423 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
4424 simulate_typing_characters(hwnd, "e four five six");
4425 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4426 ok (result == TRUE, "Failed to undo typed characters.\n");
4427 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4428 result = strcmp(buffer, "");
4429 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4430
4431 /* Test the effect of the delete key during typing on undo transactions */
4432 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4433 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
4434 ok(result == TRUE, "Failed to set the text.\n");
4435 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
4436 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4437 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4438 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4439 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4440 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4441 ok (result == TRUE, "Failed to undo typed characters.\n");
4442 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4443 result = strcmp(buffer, "acd");
4444 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
4445 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4446 ok (result == TRUE, "Failed to undo typed characters.\n");
4447 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4448 result = strcmp(buffer, "abcd");
4449 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
4450
4451 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
4452 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4453 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4454 ok (result == TRUE, "Failed to clear the text.\n");
4455 simulate_typing_characters(hwnd, "one two three");
4456 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
4457 ok (result == 0, "expected %d but got %d\n", 0, result);
4458 simulate_typing_characters(hwnd, " four five six");
4459 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4460 ok (result == TRUE, "Failed to undo typed characters.\n");
4461 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4462 result = strcmp(buffer, "one two three");
4463 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4464 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4465 ok (result == TRUE, "Failed to undo typed characters.\n");
4466 ok (result == TRUE, "Failed to undo typed characters.\n");
4467 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4468 result = strcmp(buffer, "");
4469 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4470
4471 DestroyWindow(hwnd);
4472 }
4473
4474 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
4475 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
4476
4477 static void test_word_movement(void)
4478 {
4479 HWND hwnd;
4480 int result;
4481 int sel_start, sel_end;
4482
4483 /* multi-line control inserts CR normally */
4484 hwnd = new_richedit(NULL);
4485
4486 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
4487 ok (result == TRUE, "Failed to clear the text.\n");
4488 SendMessage(hwnd, EM_SETSEL, 0, 0);
4489 /* |one two three */
4490
4491 SEND_CTRL_RIGHT(hwnd);
4492 /* one |two three */
4493 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4494 ok(sel_start == sel_end, "Selection should be empty\n");
4495 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4496
4497 SEND_CTRL_RIGHT(hwnd);
4498 /* one two |three */
4499 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4500 ok(sel_start == sel_end, "Selection should be empty\n");
4501 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4502
4503 SEND_CTRL_LEFT(hwnd);
4504 /* one |two three */
4505 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4506 ok(sel_start == sel_end, "Selection should be empty\n");
4507 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4508
4509 SEND_CTRL_LEFT(hwnd);
4510 /* |one two three */
4511 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4512 ok(sel_start == sel_end, "Selection should be empty\n");
4513 ok(sel_start == 0, "Cursur is at %d instead of %d\n", sel_start, 0);
4514
4515 SendMessage(hwnd, EM_SETSEL, 8, 8);
4516 /* one two | three */
4517 SEND_CTRL_RIGHT(hwnd);
4518 /* one two |three */
4519 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4520 ok(sel_start == sel_end, "Selection should be empty\n");
4521 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4522
4523 SendMessage(hwnd, EM_SETSEL, 11, 11);
4524 /* one two th|ree */
4525 SEND_CTRL_LEFT(hwnd);
4526 /* one two |three */
4527 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4528 ok(sel_start == sel_end, "Selection should be empty\n");
4529 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4530
4531 DestroyWindow(hwnd);
4532 }
4533
4534 static void test_EM_CHARFROMPOS(void)
4535 {
4536 HWND hwnd;
4537 int result;
4538 POINTL point;
4539 point.x = 0;
4540 point.y = 50;
4541
4542 /* multi-line control inserts CR normally */
4543 hwnd = new_richedit(NULL);
4544 result = SendMessageA(hwnd, WM_SETTEXT, 0,
4545 (LPARAM)"one two three four five six seven");
4546
4547 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
4548 ok(result == 0, "expected character index of 0 but got %d\n", result);
4549
4550 DestroyWindow(hwnd);
4551 }
4552
4553 START_TEST( editor )
4554 {
4555 MSG msg;
4556 time_t end;
4557
4558 /* Must explicitly LoadLibrary(). The test has no references to functions in
4559 * RICHED20.DLL, so the linker doesn't actually link to it. */
4560 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4561 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4562 test_WM_CHAR();
4563 test_EM_FINDTEXT();
4564 test_EM_GETLINE();
4565 test_EM_POSFROMCHAR();
4566 test_EM_SCROLLCARET();
4567 test_EM_SCROLL();
4568 test_WM_SETTEXT();
4569 test_EM_LINELENGTH();
4570 test_EM_SETCHARFORMAT();
4571 test_EM_SETTEXTMODE();
4572 test_TM_PLAINTEXT();
4573 test_EM_SETOPTIONS();
4574 test_WM_GETTEXT();
4575 test_EM_GETTEXTRANGE();
4576 test_EM_GETSELTEXT();
4577 test_EM_SETUNDOLIMIT();
4578 test_ES_PASSWORD();
4579 test_EM_SETTEXTEX();
4580 test_EM_LIMITTEXT();
4581 test_EM_EXLIMITTEXT();
4582 test_EM_GETLIMITTEXT();
4583 test_WM_SETFONT();
4584 test_EM_GETMODIFY();
4585 test_EM_EXSETSEL();
4586 test_WM_PASTE();
4587 test_EM_STREAMIN();
4588 test_EM_STREAMOUT();
4589 test_EM_StreamIn_Undo();
4590 test_EM_FORMATRANGE();
4591 test_unicode_conversions();
4592 test_EM_GETTEXTLENGTHEX();
4593 test_EM_REPLACESEL(1);
4594 test_EM_REPLACESEL(0);
4595 test_WM_NOTIFY();
4596 test_EM_AUTOURLDETECT();
4597 test_eventMask();
4598 test_undo_coalescing();
4599 test_word_movement();
4600 test_EM_CHARFROMPOS();
4601
4602 /* Set the environment variable WINETEST_RICHED20 to keep windows
4603 * responsive and open for 30 seconds. This is useful for debugging.
4604 *
4605 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4606 * 50ms before retrying the queue. */
4607 end = time(NULL) + 30;
4608 if (getenv( "WINETEST_RICHED20" )) {
4609 while (time(NULL) < end) {
4610 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4611 TranslateMessage(&msg);
4612 DispatchMessage(&msg);
4613 } else {
4614 Sleep(50);
4615 }
4616 }
4617 }
4618
4619 OleFlushClipboard();
4620 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
4621 }