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 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 /* DO NOT change the properties of the first two elements. To shorten the
1387 tests, all tests after WM_SETTEXT test just the first two elements -
1388 one non-URL and one URL */
1389 struct urls_s {
1390 const char *text;
1391 int is_url;
1392 } urls[12] = {
1393 {"winehq.org", 0},
1394 {"http://www.winehq.org", 1},
1395 {"http//winehq.org", 0},
1396 {"ww.winehq.org", 0},
1397 {"www.winehq.org", 1},
1398 {"ftp://192.168.1.1", 1},
1399 {"ftp//192.168.1.1", 0},
1400 {"mailto:your@email.com", 1},
1401 {"prospero:prosperoserver", 1},
1402 {"telnet:test", 1},
1403 {"news:newserver", 1},
1404 {"wais:waisserver", 1}
1405 };
1406
1407 int i, j;
1408 int urlRet=-1;
1409 HWND hwndRichEdit, parent;
1410
1411 /* All of the following should cause the URL to be detected */
1412 const char * templates_delim[] = {
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\r on it",
1416 "This is some text with ---X--- on it",
1417 "This is some text with \"X\" on it",
1418 "This is some text with 'X' on it",
1419 "This is some text with 'X' on it",
1420 "This is some text with :X: on it",
1421
1422 "This text ends with X",
1423
1424 "This is some text with X) on it",
1425 "This is some text with X--- on it",
1426 "This is some text with X\" on it",
1427 "This is some text with X' on it",
1428 "This is some text with X: on it",
1429
1430 "This is some text with (X on it",
1431 "This is some text with \rX on it",
1432 "This is some text with ---X on it",
1433 "This is some text with \"X on it",
1434 "This is some text with 'X on it",
1435 "This is some text with :X on it",
1436 };
1437 /* None of these should cause the URL to be detected */
1438 const char * templates_non_delim[] = {
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 "This is some text with #X on it",
1453 "This is some text with @X on it",
1454 "This is some text with \\X on it",
1455 };
1456 /* All of these cause the URL detection to be extended by one more byte,
1457 thus demonstrating that the tested character is considered as part
1458 of the URL. */
1459 const char * templates_xten_delim[] = {
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 "This is some text with X# on it",
1466 "This is some text with X@ on it",
1467 "This is some text with X\\ on it",
1468 };
1469 char buffer[1024];
1470
1471 parent = new_static_wnd(NULL);
1472 hwndRichEdit = new_richedit(parent);
1473 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1474 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1475 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1476 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1477 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1478 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1479 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1480 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1481 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1482 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1483 /* for each url, check the text to see if CFE_LINK effect is present */
1484 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1485
1486 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1488 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1489
1490 /* Link detection should happen immediately upon WM_SETTEXT */
1491 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1492 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1493 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1494 }
1495 DestroyWindow(hwndRichEdit);
1496
1497 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1498 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1499 hwndRichEdit = new_richedit(parent);
1500
1501 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1502 char * at_pos;
1503 int at_offset;
1504 int end_offset;
1505
1506 at_pos = strchr(templates_delim[j], 'X');
1507 at_offset = at_pos - templates_delim[j];
1508 strncpy(buffer, templates_delim[j], at_offset);
1509 buffer[at_offset] = '\0';
1510 strcat(buffer, urls[i].text);
1511 strcat(buffer, templates_delim[j] + at_offset + 1);
1512 end_offset = at_offset + strlen(urls[i].text);
1513
1514 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1516
1517 /* This assumes no templates start with the URL itself, and that they
1518 have at least two characters before the URL text */
1519 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1520 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1521 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1522 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1523 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1524 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1525
1526 if (urls[i].is_url)
1527 {
1528 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1529 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1530 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1531 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1532 }
1533 else
1534 {
1535 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1536 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1537 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1538 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1539 }
1540 if (buffer[end_offset] != '\0')
1541 {
1542 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1543 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1544 if (buffer[end_offset +1] != '\0')
1545 {
1546 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1547 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1548 }
1549 }
1550 }
1551
1552 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1553 char * at_pos;
1554 int at_offset;
1555 int end_offset;
1556
1557 at_pos = strchr(templates_non_delim[j], 'X');
1558 at_offset = at_pos - templates_non_delim[j];
1559 strncpy(buffer, templates_non_delim[j], at_offset);
1560 buffer[at_offset] = '\0';
1561 strcat(buffer, urls[i].text);
1562 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1563 end_offset = at_offset + strlen(urls[i].text);
1564
1565 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1566 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1567
1568 /* This assumes no templates start with the URL itself, and that they
1569 have at least two characters before the URL text */
1570 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1571 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1572 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1573 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1574 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1576
1577 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1578 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1579 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1580 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1581 if (buffer[end_offset] != '\0')
1582 {
1583 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1584 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1585 if (buffer[end_offset +1] != '\0')
1586 {
1587 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1588 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1589 }
1590 }
1591 }
1592
1593 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1594 char * at_pos;
1595 int at_offset;
1596 int end_offset;
1597
1598 at_pos = strchr(templates_xten_delim[j], 'X');
1599 at_offset = at_pos - templates_xten_delim[j];
1600 strncpy(buffer, templates_xten_delim[j], at_offset);
1601 buffer[at_offset] = '\0';
1602 strcat(buffer, urls[i].text);
1603 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1604 end_offset = at_offset + strlen(urls[i].text);
1605
1606 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1608
1609 /* This assumes no templates start with the URL itself, and that they
1610 have at least two characters before the URL text */
1611 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1612 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1613 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1615 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1616 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1617
1618 if (urls[i].is_url)
1619 {
1620 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1621 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1622 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1623 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1624 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1625 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1626 }
1627 else
1628 {
1629 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1630 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1631 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1632 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1633 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1634 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1635 }
1636 if (buffer[end_offset +1] != '\0')
1637 {
1638 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1640 if (buffer[end_offset +2] != '\0')
1641 {
1642 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1643 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1644 }
1645 }
1646 }
1647
1648 DestroyWindow(hwndRichEdit);
1649 hwndRichEdit = NULL;
1650 }
1651
1652 /* Test detection of URLs within normal text - WM_CHAR case. */
1653 /* Test only the first two URL examples for brevity */
1654 for (i = 0; i < 2; i++) {
1655 hwndRichEdit = new_richedit(parent);
1656
1657 /* Also for brevity, test only the first three delimiters */
1658 for (j = 0; j < 3; j++) {
1659 char * at_pos;
1660 int at_offset;
1661 int end_offset;
1662 int u, v;
1663
1664 at_pos = strchr(templates_delim[j], 'X');
1665 at_offset = at_pos - templates_delim[j];
1666 end_offset = at_offset + strlen(urls[i].text);
1667
1668 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1670 for (u = 0; templates_delim[j][u]; u++) {
1671 if (templates_delim[j][u] == '\r') {
1672 simulate_typing_characters(hwndRichEdit, "\r");
1673 } else if (templates_delim[j][u] != 'X') {
1674 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1675 } else {
1676 for (v = 0; urls[i].text[v]; v++) {
1677 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1678 }
1679 }
1680 }
1681 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1682
1683 /* This assumes no templates start with the URL itself, and that they
1684 have at least two characters before the URL text */
1685 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1687 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1688 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1689 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1690 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1691
1692 if (urls[i].is_url)
1693 {
1694 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1695 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1696 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1697 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1698 }
1699 else
1700 {
1701 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1702 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1703 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1704 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1705 }
1706 if (buffer[end_offset] != '\0')
1707 {
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1710 if (buffer[end_offset +1] != '\0')
1711 {
1712 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1713 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1714 }
1715 }
1716
1717 /* The following will insert a paragraph break after the first character
1718 of the URL candidate, thus breaking the URL. It is expected that the
1719 CFE_LINK attribute should break across both pieces of the URL */
1720 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1721 simulate_typing_characters(hwndRichEdit, "\r");
1722 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1723
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1726 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1727 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1728 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1729 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1730
1731 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1732 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1733 /* end_offset moved because of paragraph break */
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1736 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1737 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1738 {
1739 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1740 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1741 if (buffer[end_offset +2] != '\0')
1742 {
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1745 }
1746 }
1747
1748 /* The following will remove the just-inserted paragraph break, thus
1749 restoring the URL */
1750 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1751 simulate_typing_characters(hwndRichEdit, "\b");
1752 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1753
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1756 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1757 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1760
1761 if (urls[i].is_url)
1762 {
1763 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1764 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1765 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1766 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1767 }
1768 else
1769 {
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1772 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1773 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1774 }
1775 if (buffer[end_offset] != '\0')
1776 {
1777 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1778 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1779 if (buffer[end_offset +1] != '\0')
1780 {
1781 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1782 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1783 }
1784 }
1785 }
1786 DestroyWindow(hwndRichEdit);
1787 hwndRichEdit = NULL;
1788 }
1789
1790 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1791 /* Test just the first two URL examples for brevity */
1792 for (i = 0; i < 2; i++) {
1793 SETTEXTEX st;
1794
1795 hwndRichEdit = new_richedit(parent);
1796
1797 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1798 be detected:
1799 1) Set entire text, a la WM_SETTEXT
1800 2) Set a selection of the text to the URL
1801 3) Set a portion of the text at a time, which eventually results in
1802 an URL
1803 All of them should give equivalent results
1804 */
1805
1806 /* Set entire text in one go, like WM_SETTEXT */
1807 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1808 char * at_pos;
1809 int at_offset;
1810 int end_offset;
1811
1812 st.codepage = CP_ACP;
1813 st.flags = ST_DEFAULT;
1814
1815 at_pos = strchr(templates_delim[j], 'X');
1816 at_offset = at_pos - templates_delim[j];
1817 strncpy(buffer, templates_delim[j], at_offset);
1818 buffer[at_offset] = '\0';
1819 strcat(buffer, urls[i].text);
1820 strcat(buffer, templates_delim[j] + at_offset + 1);
1821 end_offset = at_offset + strlen(urls[i].text);
1822
1823 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1824 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1825
1826 /* This assumes no templates start with the URL itself, and that they
1827 have at least two characters before the URL text */
1828 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1829 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1834
1835 if (urls[i].is_url)
1836 {
1837 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1839 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1841 }
1842 else
1843 {
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1848 }
1849 if (buffer[end_offset] != '\0')
1850 {
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1854 {
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1857 }
1858 }
1859 }
1860
1861 /* Set selection with X to the URL */
1862 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1863 char * at_pos;
1864 int at_offset;
1865 int end_offset;
1866
1867 at_pos = strchr(templates_delim[j], 'X');
1868 at_offset = at_pos - templates_delim[j];
1869 end_offset = at_offset + strlen(urls[i].text);
1870
1871 st.codepage = CP_ACP;
1872 st.flags = ST_DEFAULT;
1873 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1874 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1875 st.flags = ST_SELECTION;
1876 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1877 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1878 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1879
1880 /* This assumes no templates start with the URL itself, and that they
1881 have at least two characters before the URL text */
1882 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1883 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1884 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1885 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1888
1889 if (urls[i].is_url)
1890 {
1891 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1892 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1893 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1894 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1895 }
1896 else
1897 {
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1902 }
1903 if (buffer[end_offset] != '\0')
1904 {
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1907 if (buffer[end_offset +1] != '\0')
1908 {
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1911 }
1912 }
1913 }
1914
1915 /* Set selection with X to the first character of the URL, then the rest */
1916 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1917 char * at_pos;
1918 int at_offset;
1919 int end_offset;
1920
1921 at_pos = strchr(templates_delim[j], 'X');
1922 at_offset = at_pos - templates_delim[j];
1923 end_offset = at_offset + strlen(urls[i].text);
1924
1925 strcpy(buffer, "YY");
1926 buffer[0] = urls[i].text[0];
1927
1928 st.codepage = CP_ACP;
1929 st.flags = ST_DEFAULT;
1930 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1932 st.flags = ST_SELECTION;
1933 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1934 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1935 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1936 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1937 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1938
1939 /* This assumes no templates start with the URL itself, and that they
1940 have at least two characters before the URL text */
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1947
1948 if (urls[i].is_url)
1949 {
1950 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1951 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1952 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1953 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1954 }
1955 else
1956 {
1957 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1958 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1961 }
1962 if (buffer[end_offset] != '\0')
1963 {
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1966 if (buffer[end_offset +1] != '\0')
1967 {
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1970 }
1971 }
1972 }
1973
1974 DestroyWindow(hwndRichEdit);
1975 hwndRichEdit = NULL;
1976 }
1977
1978 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1979 /* Test just the first two URL examples for brevity */
1980 for (i = 0; i < 2; i++) {
1981 hwndRichEdit = new_richedit(parent);
1982
1983 /* Set selection with X to the URL */
1984 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1985 char * at_pos;
1986 int at_offset;
1987 int end_offset;
1988
1989 at_pos = strchr(templates_delim[j], 'X');
1990 at_offset = at_pos - templates_delim[j];
1991 end_offset = at_offset + strlen(urls[i].text);
1992
1993 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1994 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1995 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1996 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1997 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1998
1999 /* This assumes no templates start with the URL itself, and that they
2000 have at least two characters before the URL text */
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2007
2008 if (urls[i].is_url)
2009 {
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2014 }
2015 else
2016 {
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2021 }
2022 if (buffer[end_offset] != '\0')
2023 {
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2027 {
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2030 }
2031 }
2032 }
2033
2034 /* Set selection with X to the first character of the URL, then the rest */
2035 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2036 char * at_pos;
2037 int at_offset;
2038 int end_offset;
2039
2040 at_pos = strchr(templates_delim[j], 'X');
2041 at_offset = at_pos - templates_delim[j];
2042 end_offset = at_offset + strlen(urls[i].text);
2043
2044 strcpy(buffer, "YY");
2045 buffer[0] = urls[i].text[0];
2046
2047 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2048 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2049 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2050 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2051 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2052 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2053 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2054
2055 /* This assumes no templates start with the URL itself, and that they
2056 have at least two characters before the URL text */
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2059 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2060 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2063
2064 if (urls[i].is_url)
2065 {
2066 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2067 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2068 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2069 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2070 }
2071 else
2072 {
2073 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2074 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2077 }
2078 if (buffer[end_offset] != '\0')
2079 {
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2082 if (buffer[end_offset +1] != '\0')
2083 {
2084 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2085 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2086 }
2087 }
2088 }
2089
2090 DestroyWindow(hwndRichEdit);
2091 hwndRichEdit = NULL;
2092 }
2093
2094 DestroyWindow(parent);
2095 }
2096
2097 static void test_EM_SCROLL(void)
2098 {
2099 int i, j;
2100 int r; /* return value */
2101 int expr; /* expected return value */
2102 HWND hwndRichEdit = new_richedit(NULL);
2103 int y_before, y_after; /* units of lines of text */
2104
2105 /* test a richedit box containing a single line of text */
2106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2107 expr = 0x00010000;
2108 for (i = 0; i < 4; i++) {
2109 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2110
2111 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2112 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2113 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2114 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2115 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2116 "(i == %d)\n", y_after, i);
2117 }
2118
2119 /*
2120 * test a richedit box that will scroll. There are two general
2121 * cases: the case without any long lines and the case with a long
2122 * line.
2123 */
2124 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2125 if (i == 0)
2126 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2127 else
2128 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2129 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2130 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2131 "LONG LINE \nb\nc\nd\ne");
2132 for (j = 0; j < 12; j++) /* reset scroll position to top */
2133 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2134
2135 /* get first visible line */
2136 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2137 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2138
2139 /* get new current first visible line */
2140 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2141
2142 ok(((r & 0xffffff00) == 0x00010000) &&
2143 ((r & 0x000000ff) != 0x00000000),
2144 "EM_SCROLL page down didn't scroll by a small positive number of "
2145 "lines (r == 0x%08x)\n", r);
2146 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2147 "(line %d scrolled to line %d\n", y_before, y_after);
2148
2149 y_before = y_after;
2150
2151 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2152 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2153 ok(((r & 0xffffff00) == 0x0001ff00),
2154 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2155 "(r == 0x%08x)\n", r);
2156 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2157 "%d scrolled to line %d\n", y_before, y_after);
2158
2159 y_before = y_after;
2160
2161 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2162
2163 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2164
2165 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2166 "(r == 0x%08x)\n", r);
2167 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2168 "1 line (%d scrolled to %d)\n", y_before, y_after);
2169
2170 y_before = y_after;
2171
2172 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2173
2174 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2175
2176 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2177 "(r == 0x%08x)\n", r);
2178 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2179 "line (%d scrolled to %d)\n", y_before, y_after);
2180
2181 y_before = y_after;
2182
2183 r = SendMessage(hwndRichEdit, EM_SCROLL,
2184 SB_LINEUP, 0); /* lineup beyond top */
2185
2186 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2187
2188 ok(r == 0x00010000,
2189 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2190 ok(y_before == y_after,
2191 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2192
2193 y_before = y_after;
2194
2195 r = SendMessage(hwndRichEdit, EM_SCROLL,
2196 SB_PAGEUP, 0);/*page up beyond top */
2197
2198 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2199
2200 ok(r == 0x00010000,
2201 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2202 ok(y_before == y_after,
2203 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2204
2205 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2206 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2207 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2208 r = SendMessage(hwndRichEdit, EM_SCROLL,
2209 SB_PAGEDOWN, 0); /* page down beyond bot */
2210 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2211
2212 ok(r == 0x00010000,
2213 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2214 ok(y_before == y_after,
2215 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2216 y_before, y_after);
2217
2218 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2219 SendMessage(hwndRichEdit, EM_SCROLL,
2220 SB_LINEDOWN, 0); /* line down beyond bot */
2221 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2222
2223 ok(r == 0x00010000,
2224 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2225 ok(y_before == y_after,
2226 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2227 y_before, y_after);
2228 }
2229 DestroyWindow(hwndRichEdit);
2230 }
2231
2232 unsigned int recursionLevel = 0;
2233 unsigned int WM_SIZE_recursionLevel = 0;
2234 BOOL bailedOutOfRecursion = FALSE;
2235 LRESULT WINAPI (*richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2236
2237 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2238 {
2239 LRESULT r;
2240
2241 if (bailedOutOfRecursion) return 0;
2242 if (recursionLevel >= 32) {
2243 bailedOutOfRecursion = TRUE;
2244 return 0;
2245 }
2246
2247 recursionLevel++;
2248 switch (message) {
2249 case WM_SIZE:
2250 WM_SIZE_recursionLevel++;
2251 r = richeditProc(hwnd, message, wParam, lParam);
2252 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2253 ShowScrollBar(hwnd, SB_VERT, TRUE);
2254 WM_SIZE_recursionLevel--;
2255 break;
2256 default:
2257 r = richeditProc(hwnd, message, wParam, lParam);
2258 break;
2259 }
2260 recursionLevel--;
2261 return r;
2262 }
2263
2264 static void test_scrollbar_visibility(void)
2265 {
2266 HWND hwndRichEdit;
2267 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2268 SCROLLINFO si;
2269 WNDCLASSA cls;
2270 BOOL r;
2271
2272 /* These tests show that richedit should temporarily refrain from automatically
2273 hiding or showing its scrollbars (vertical at least) when an explicit request
2274 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2275 Some applications depend on forced showing (when otherwise richedit would
2276 hide the vertical scrollbar) and are thrown on an endless recursive loop
2277 if richedit auto-hides the scrollbar again. Apparently they never heard of
2278 the ES_DISABLENOSCROLL style... */
2279
2280 hwndRichEdit = new_richedit(NULL);
2281
2282 /* Test default scrollbar visibility behavior */
2283 memset(&si, 0, sizeof(si));
2284 si.cbSize = sizeof(si);
2285 si.fMask = SIF_PAGE | SIF_RANGE;
2286 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2287 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2288 "Vertical scrollbar is visible, should be invisible.\n");
2289 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2290 "reported page/range is %d (%d..%d) expected all 0\n",
2291 si.nPage, si.nMin, si.nMax);
2292
2293 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2294 memset(&si, 0, sizeof(si));
2295 si.cbSize = sizeof(si);
2296 si.fMask = SIF_PAGE | SIF_RANGE;
2297 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2298 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2299 "Vertical scrollbar is visible, should be invisible.\n");
2300 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2301 "reported page/range is %d (%d..%d) expected all 0\n",
2302 si.nPage, si.nMin, si.nMax);
2303
2304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2305 memset(&si, 0, sizeof(si));
2306 si.cbSize = sizeof(si);
2307 si.fMask = SIF_PAGE | SIF_RANGE;
2308 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2309 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2310 "Vertical scrollbar is invisible, should be visible.\n");
2311 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2312 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2313 si.nPage, si.nMin, si.nMax);
2314
2315 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2316 even though it hides the scrollbar */
2317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2318 memset(&si, 0, sizeof(si));
2319 si.cbSize = sizeof(si);
2320 si.fMask = SIF_PAGE | SIF_RANGE;
2321 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2322 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2323 "Vertical scrollbar is visible, should be invisible.\n");
2324 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2325 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2326 si.nPage, si.nMin, si.nMax);
2327
2328 /* Setting non-scrolling text again does *not* reset scrollbar range */
2329 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2330 memset(&si, 0, sizeof(si));
2331 si.cbSize = sizeof(si);
2332 si.fMask = SIF_PAGE | SIF_RANGE;
2333 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2334 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2335 "Vertical scrollbar is visible, should be invisible.\n");
2336 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2337 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2338 si.nPage, si.nMin, si.nMax);
2339
2340 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2341 memset(&si, 0, sizeof(si));
2342 si.cbSize = sizeof(si);
2343 si.fMask = SIF_PAGE | SIF_RANGE;
2344 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2345 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2346 "Vertical scrollbar is visible, should be invisible.\n");
2347 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2348 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2349 si.nPage, si.nMin, si.nMax);
2350
2351 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2352 memset(&si, 0, sizeof(si));
2353 si.cbSize = sizeof(si);
2354 si.fMask = SIF_PAGE | SIF_RANGE;
2355 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2356 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2357 "Vertical scrollbar is visible, should be invisible.\n");
2358 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2359 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2360 si.nPage, si.nMin, si.nMax);
2361
2362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2363 memset(&si, 0, sizeof(si));
2364 si.cbSize = sizeof(si);
2365 si.fMask = SIF_PAGE | SIF_RANGE;
2366 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2367 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2368 "Vertical scrollbar is visible, should be invisible.\n");
2369 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2370 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2371 si.nPage, si.nMin, si.nMax);
2372
2373 DestroyWindow(hwndRichEdit);
2374
2375 /* Test again, with ES_DISABLENOSCROLL style */
2376 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2377
2378 /* Test default scrollbar visibility behavior */
2379 memset(&si, 0, sizeof(si));
2380 si.cbSize = sizeof(si);
2381 si.fMask = SIF_PAGE | SIF_RANGE;
2382 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2383 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2384 "Vertical scrollbar is invisible, should be visible.\n");
2385 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2386 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2387 si.nPage, si.nMin, si.nMax);
2388
2389 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2390 memset(&si, 0, sizeof(si));
2391 si.cbSize = sizeof(si);
2392 si.fMask = SIF_PAGE | SIF_RANGE;
2393 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2394 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2395 "Vertical scrollbar is invisible, should be visible.\n");
2396 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2397 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2398 si.nPage, si.nMin, si.nMax);
2399
2400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2401 memset(&si, 0, sizeof(si));
2402 si.cbSize = sizeof(si);
2403 si.fMask = SIF_PAGE | SIF_RANGE;
2404 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2405 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2406 "Vertical scrollbar is invisible, should be visible.\n");
2407 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2408 "reported page/range is %d (%d..%d)\n",
2409 si.nPage, si.nMin, si.nMax);
2410
2411 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2412 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2413 memset(&si, 0, sizeof(si));
2414 si.cbSize = sizeof(si);
2415 si.fMask = SIF_PAGE | SIF_RANGE;
2416 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2417 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2418 "Vertical scrollbar is invisible, should be visible.\n");
2419 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2420 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2421 si.nPage, si.nMin, si.nMax);
2422
2423 /* Setting non-scrolling text again does *not* reset scrollbar range */
2424 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2425 memset(&si, 0, sizeof(si));
2426 si.cbSize = sizeof(si);
2427 si.fMask = SIF_PAGE | SIF_RANGE;
2428 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2429 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2430 "Vertical scrollbar is invisible, should be visible.\n");
2431 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2432 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2433 si.nPage, si.nMin, si.nMax);
2434
2435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2436 memset(&si, 0, sizeof(si));
2437 si.cbSize = sizeof(si);
2438 si.fMask = SIF_PAGE | SIF_RANGE;
2439 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2440 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2441 "Vertical scrollbar is invisible, should be visible.\n");
2442 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2443 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2444 si.nPage, si.nMin, si.nMax);
2445
2446 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2447 memset(&si, 0, sizeof(si));
2448 si.cbSize = sizeof(si);
2449 si.fMask = SIF_PAGE | SIF_RANGE;
2450 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2451 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2452 "Vertical scrollbar is invisible, should be visible.\n");
2453 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2454 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2455 si.nPage, si.nMin, si.nMax);
2456
2457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2458 memset(&si, 0, sizeof(si));
2459 si.cbSize = sizeof(si);
2460 si.fMask = SIF_PAGE | SIF_RANGE;
2461 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2462 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2463 "Vertical scrollbar is invisible, should be visible.\n");
2464 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2465 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2466 si.nPage, si.nMin, si.nMax);
2467
2468 DestroyWindow(hwndRichEdit);
2469
2470 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2471 hwndRichEdit = new_richedit(NULL);
2472
2473 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2474 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2475 memset(&si, 0, sizeof(si));
2476 si.cbSize = sizeof(si);
2477 si.fMask = SIF_PAGE | SIF_RANGE;
2478 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2479 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2480 "Vertical scrollbar is invisible, should be visible.\n");
2481 todo_wine {
2482 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2483 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2484 si.nPage, si.nMin, si.nMax);
2485 }
2486
2487 /* Ditto, see above */
2488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2489 memset(&si, 0, sizeof(si));
2490 si.cbSize = sizeof(si);
2491 si.fMask = SIF_PAGE | SIF_RANGE;
2492 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2493 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2494 "Vertical scrollbar is invisible, should be visible.\n");
2495 todo_wine {
2496 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2497 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2498 si.nPage, si.nMin, si.nMax);
2499 }
2500
2501 /* Ditto, see above */
2502 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2503 memset(&si, 0, sizeof(si));
2504 si.cbSize = sizeof(si);
2505 si.fMask = SIF_PAGE | SIF_RANGE;
2506 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2507 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2508 "Vertical scrollbar is invisible, should be visible.\n");
2509 todo_wine {
2510 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2511 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2512 si.nPage, si.nMin, si.nMax);
2513 }
2514
2515 /* Ditto, see above */
2516 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2517 memset(&si, 0, sizeof(si));
2518 si.cbSize = sizeof(si);
2519 si.fMask = SIF_PAGE | SIF_RANGE;
2520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2522 "Vertical scrollbar is invisible, should be visible.\n");
2523 todo_wine {
2524 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2525 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2526 si.nPage, si.nMin, si.nMax);
2527 }
2528
2529 /* Ditto, see above */
2530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2531 memset(&si, 0, sizeof(si));
2532 si.cbSize = sizeof(si);
2533 si.fMask = SIF_PAGE | SIF_RANGE;
2534 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2535 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2536 "Vertical scrollbar is invisible, should be visible.\n");
2537 todo_wine {
2538 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2539 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2540 si.nPage, si.nMin, si.nMax);
2541 }
2542
2543 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2544 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2545 memset(&si, 0, sizeof(si));
2546 si.cbSize = sizeof(si);
2547 si.fMask = SIF_PAGE | SIF_RANGE;
2548 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2549 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2550 "Vertical scrollbar is visible, should be invisible.\n");
2551 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2552 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2553 si.nPage, si.nMin, si.nMax);
2554
2555 DestroyWindow(hwndRichEdit);
2556
2557 hwndRichEdit = new_richedit(NULL);
2558
2559 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2560 memset(&si, 0, sizeof(si));
2561 si.cbSize = sizeof(si);
2562 si.fMask = SIF_PAGE | SIF_RANGE;
2563 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2564 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2565 "Vertical scrollbar is visible, should be invisible.\n");
2566 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2567 "reported page/range is %d (%d..%d) expected all 0\n",
2568 si.nPage, si.nMin, si.nMax);
2569
2570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2571 memset(&si, 0, sizeof(si));
2572 si.cbSize = sizeof(si);
2573 si.fMask = SIF_PAGE | SIF_RANGE;
2574 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2575 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2576 "Vertical scrollbar is visible, should be invisible.\n");
2577 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2578 "reported page/range is %d (%d..%d) expected all 0\n",
2579 si.nPage, si.nMin, si.nMax);
2580
2581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2582 memset(&si, 0, sizeof(si));
2583 si.cbSize = sizeof(si);
2584 si.fMask = SIF_PAGE | SIF_RANGE;
2585 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2586 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2587 "Vertical scrollbar is visible, should be invisible.\n");
2588 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2589 "reported page/range is %d (%d..%d) expected all 0\n",
2590 si.nPage, si.nMin, si.nMax);
2591
2592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2593 memset(&si, 0, sizeof(si));
2594 si.cbSize = sizeof(si);
2595 si.fMask = SIF_PAGE | SIF_RANGE;
2596 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2597 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2598 "Vertical scrollbar is visible, should be invisible.\n");
2599 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2600 "reported page/range is %d (%d..%d) expected all 0\n",
2601 si.nPage, si.nMin, si.nMax);
2602
2603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2604 memset(&si, 0, sizeof(si));
2605 si.cbSize = sizeof(si);
2606 si.fMask = SIF_PAGE | SIF_RANGE;
2607 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2608 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2609 "Vertical scrollbar is invisible, should be visible.\n");
2610 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2611 "reported page/range is %d (%d..%d)\n",
2612 si.nPage, si.nMin, si.nMax);
2613
2614 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2615 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2616 memset(&si, 0, sizeof(si));
2617 si.cbSize = sizeof(si);
2618 si.fMask = SIF_PAGE | SIF_RANGE;
2619 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2620 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2621 "Vertical scrollbar is visible, should be invisible.\n");
2622 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2623 "reported page/range is %d (%d..%d)\n",
2624 si.nPage, si.nMin, si.nMax);
2625
2626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2627 memset(&si, 0, sizeof(si));
2628 si.cbSize = sizeof(si);
2629 si.fMask = SIF_PAGE | SIF_RANGE;
2630 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2631 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2632 "Vertical scrollbar is visible, should be invisible.\n");
2633 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2634 "reported page/range is %d (%d..%d)\n",
2635 si.nPage, si.nMin, si.nMax);
2636
2637 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2638 EM_SCROLL will make visible any forcefully invisible scrollbar */
2639 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2640 memset(&si, 0, sizeof(si));
2641 si.cbSize = sizeof(si);
2642 si.fMask = SIF_PAGE | SIF_RANGE;
2643 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2644 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2645 "Vertical scrollbar is invisible, should be visible.\n");
2646 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2647 "reported page/range is %d (%d..%d)\n",
2648 si.nPage, si.nMin, si.nMax);
2649
2650 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2651 memset(&si, 0, sizeof(si));
2652 si.cbSize = sizeof(si);
2653 si.fMask = SIF_PAGE | SIF_RANGE;
2654 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2655 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2656 "Vertical scrollbar is visible, should be invisible.\n");
2657 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2658 "reported page/range is %d (%d..%d)\n",
2659 si.nPage, si.nMin, si.nMax);
2660
2661 /* Again, EM_SCROLL, with SB_LINEUP */
2662 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2663 memset(&si, 0, sizeof(si));
2664 si.cbSize = sizeof(si);
2665 si.fMask = SIF_PAGE | SIF_RANGE;
2666 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2667 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2668 "Vertical scrollbar is invisible, should be visible.\n");
2669 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2670 "reported page/range is %d (%d..%d)\n",
2671 si.nPage, si.nMin, si.nMax);
2672
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2674 memset(&si, 0, sizeof(si));
2675 si.cbSize = sizeof(si);
2676 si.fMask = SIF_PAGE | SIF_RANGE;
2677 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2678 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2679 "Vertical scrollbar is visible, should be invisible.\n");
2680 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2681 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2682 si.nPage, si.nMin, si.nMax);
2683
2684 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2685 memset(&si, 0, sizeof(si));
2686 si.cbSize = sizeof(si);
2687 si.fMask = SIF_PAGE | SIF_RANGE;
2688 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2689 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2690 "Vertical scrollbar is invisible, should be visible.\n");
2691 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2692 "reported page/range is %d (%d..%d)\n",
2693 si.nPage, si.nMin, si.nMax);
2694
2695 DestroyWindow(hwndRichEdit);
2696
2697
2698 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2699 hwndRichEdit = new_richedit(NULL);
2700
2701 #define ENABLE_WS_VSCROLL(hwnd) \
2702 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2703 #define DISABLE_WS_VSCROLL(hwnd) \
2704 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2705
2706 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2707 ENABLE_WS_VSCROLL(hwndRichEdit);
2708 memset(&si, 0, sizeof(si));
2709 si.cbSize = sizeof(si);
2710 si.fMask = SIF_PAGE | SIF_RANGE;
2711 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2712 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2713 "Vertical scrollbar is invisible, should be visible.\n");
2714 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2715 "reported page/range is %d (%d..%d) expected all 0\n",
2716 si.nPage, si.nMin, si.nMax);
2717
2718 /* Ditto, see above */
2719 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2720 memset(&si, 0, sizeof(si));
2721 si.cbSize = sizeof(si);
2722 si.fMask = SIF_PAGE | SIF_RANGE;
2723 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2724 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2725 "Vertical scrollbar is invisible, should be visible.\n");
2726 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2727 "reported page/range is %d (%d..%d) expected all 0\n",
2728 si.nPage, si.nMin, si.nMax);
2729
2730 /* Ditto, see above */
2731 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2732 memset(&si, 0, sizeof(si));
2733 si.cbSize = sizeof(si);
2734 si.fMask = SIF_PAGE | SIF_RANGE;
2735 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2736 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2737 "Vertical scrollbar is invisible, should be visible.\n");
2738 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2739 "reported page/range is %d (%d..%d) expected all 0\n",
2740 si.nPage, si.nMin, si.nMax);
2741
2742 /* Ditto, see above */
2743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2744 memset(&si, 0, sizeof(si));
2745 si.cbSize = sizeof(si);
2746 si.fMask = SIF_PAGE | SIF_RANGE;
2747 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2748 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2749 "Vertical scrollbar is invisible, should be visible.\n");
2750 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2751 "reported page/range is %d (%d..%d) expected all 0\n",
2752 si.nPage, si.nMin, si.nMax);
2753
2754 /* Ditto, see above */
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2761 "Vertical scrollbar is invisible, should be visible.\n");
2762 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2763 "reported page/range is %d (%d..%d) expected all 0\n",
2764 si.nPage, si.nMin, si.nMax);
2765
2766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773 "Vertical scrollbar is visible, should be invisible.\n");
2774 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776 si.nPage, si.nMin, si.nMax);
2777
2778 DestroyWindow(hwndRichEdit);
2779
2780 hwndRichEdit = new_richedit(NULL);
2781
2782 DISABLE_WS_VSCROLL(hwndRichEdit);
2783 memset(&si, 0, sizeof(si));
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_PAGE | SIF_RANGE;
2786 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788 "Vertical scrollbar is visible, should be invisible.\n");
2789 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2790 "reported page/range is %d (%d..%d) expected all 0\n",
2791 si.nPage, si.nMin, si.nMax);
2792
2793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2794 memset(&si, 0, sizeof(si));
2795 si.cbSize = sizeof(si);
2796 si.fMask = SIF_PAGE | SIF_RANGE;
2797 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2798 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2799 "Vertical scrollbar is visible, should be invisible.\n");
2800 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2801 "reported page/range is %d (%d..%d) expected all 0\n",
2802 si.nPage, si.nMin, si.nMax);
2803
2804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2805 memset(&si, 0, sizeof(si));
2806 si.cbSize = sizeof(si);
2807 si.fMask = SIF_PAGE | SIF_RANGE;
2808 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2809 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2810 "Vertical scrollbar is visible, should be invisible.\n");
2811 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2812 "reported page/range is %d (%d..%d) expected all 0\n",
2813 si.nPage, si.nMin, si.nMax);
2814
2815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2816 memset(&si, 0, sizeof(si));
2817 si.cbSize = sizeof(si);
2818 si.fMask = SIF_PAGE | SIF_RANGE;
2819 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2821 "Vertical scrollbar is visible, should be invisible.\n");
2822 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2823 "reported page/range is %d (%d..%d) expected all 0\n",
2824 si.nPage, si.nMin, si.nMax);
2825
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2832 "Vertical scrollbar is invisible, should be visible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2836
2837 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2838 DISABLE_WS_VSCROLL(hwndRichEdit);
2839 memset(&si, 0, sizeof(si));
2840 si.cbSize = sizeof(si);
2841 si.fMask = SIF_PAGE | SIF_RANGE;
2842 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2843 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2844 "Vertical scrollbar is visible, should be invisible.\n");
2845 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2846 "reported page/range is %d (%d..%d)\n",
2847 si.nPage, si.nMin, si.nMax);
2848
2849 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2850 memset(&si, 0, sizeof(si));
2851 si.cbSize = sizeof(si);
2852 si.fMask = SIF_PAGE | SIF_RANGE;
2853 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2855 "Vertical scrollbar is visible, should be invisible.\n");
2856 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2857 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2858 si.nPage, si.nMin, si.nMax);
2859
2860 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2861 memset(&si, 0, sizeof(si));
2862 si.cbSize = sizeof(si);
2863 si.fMask = SIF_PAGE | SIF_RANGE;
2864 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2865 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2866 "Vertical scrollbar is invisible, should be visible.\n");
2867 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2868 "reported page/range is %d (%d..%d)\n",
2869 si.nPage, si.nMin, si.nMax);
2870
2871 DISABLE_WS_VSCROLL(hwndRichEdit);
2872 memset(&si, 0, sizeof(si));
2873 si.cbSize = sizeof(si);
2874 si.fMask = SIF_PAGE | SIF_RANGE;
2875 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2876 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2877 "Vertical scrollbar is visible, should be invisible.\n");
2878 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2879 "reported page/range is %d (%d..%d)\n",
2880 si.nPage, si.nMin, si.nMax);
2881
2882 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2883 EM_SCROLL will make visible any forcefully invisible scrollbar */
2884 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2890 "Vertical scrollbar is invisible, should be visible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d)\n",
2893 si.nPage, si.nMin, si.nMax);
2894
2895 DISABLE_WS_VSCROLL(hwndRichEdit);
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2901 "Vertical scrollbar is visible, should be invisible.\n");
2902 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903 "reported page/range is %d (%d..%d)\n",
2904 si.nPage, si.nMin, si.nMax);
2905
2906 /* Again, EM_SCROLL, with SB_LINEUP */
2907 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913 "Vertical scrollbar is invisible, should be visible.\n");
2914 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2915 "reported page/range is %d (%d..%d)\n",
2916 si.nPage, si.nMin, si.nMax);
2917
2918 DestroyWindow(hwndRichEdit);
2919
2920 /* This window proc models what is going on with Corman Lisp 3.0.
2921 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2922 force the scrollbar into visibility. Recursion should NOT happen
2923 as a result of this action.
2924 */
2925 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2926 if (r) {
2927 richeditProc = cls.lpfnWndProc;
2928 cls.lpfnWndProc = RicheditStupidOverrideProcA;
2929 cls.lpszClassName = "RicheditStupidOverride";
2930 if(!RegisterClassA(&cls)) assert(0);
2931
2932 recursionLevel = 0;
2933 WM_SIZE_recursionLevel = 0;
2934 bailedOutOfRecursion = FALSE;
2935 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2936 ok(!bailedOutOfRecursion,
2937 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2938
2939 recursionLevel = 0;
2940 WM_SIZE_recursionLevel = 0;
2941 bailedOutOfRecursion = FALSE;
2942 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2943 ok(!bailedOutOfRecursion,
2944 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2945
2946 /* Unblock window in order to process WM_DESTROY */
2947 recursionLevel = 0;
2948 bailedOutOfRecursion = FALSE;
2949 WM_SIZE_recursionLevel = 0;
2950 DestroyWindow(hwndRichEdit);
2951 }
2952 }
2953
2954 static void test_EM_SETUNDOLIMIT(void)
2955 {
2956 /* cases we test for:
2957 * default behaviour - limiting at 100 undo's
2958 * undo disabled - setting a limit of 0
2959 * undo limited - undo limit set to some to some number, like 2
2960 * bad input - sending a negative number should default to 100 undo's */
2961
2962 HWND hwndRichEdit = new_richedit(NULL);
2963 CHARRANGE cr;
2964 int i;
2965 int result;
2966
2967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2968 cr.cpMin = 0;
2969 cr.cpMax = 1;
2970 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2971 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2972 also, multiple pastes don't combine like WM_CHAR would */
2973 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2974
2975 /* first case - check the default */
2976 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2977 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2978 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2979 for (i=0; i<100; i++) /* Undo 100 of them */
2980 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2981 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2982 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2983
2984 /* second case - cannot undo */
2985 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2986 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2987 SendMessage(hwndRichEdit,
2988 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2989 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2990 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2991
2992 /* third case - set it to an arbitrary number */
2993 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2994 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2995 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2996 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2997 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2998 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2999 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3000 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3001 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3002 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3003 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3004 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3005 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3006 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3007
3008 /* fourth case - setting negative numbers should default to 100 undos */
3009 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3010 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3011 ok (result == 100,
3012 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3013