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