~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/riched20/tests/editor.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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 CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
 36 
 37 #define ok_w3(format, szString1, szString2, szString3) \
 38     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
 39     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
 40     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
 41     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
 42        format, string1, string2, string3);
 43 
 44 static HMODULE hmoduleRichEdit;
 45 
 46 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
 47   HWND hwnd;
 48   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
 49                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
 50                       hmoduleRichEdit, NULL);
 51   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
 52   return hwnd;
 53 }
 54 
 55 static HWND new_richedit(HWND parent) {
 56   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
 57 }
 58 
 59 static void processPendingMessages(void)
 60 {
 61     MSG msg;
 62     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
 63         TranslateMessage(&msg);
 64         DispatchMessage(&msg);
 65     }
 66 }
 67 
 68 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
 69 {
 70     BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
 71     BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
 72     SetFocus(hwnd);
 73     keybd_event(mod_vk, mod_scan_code, 0, 0);
 74     keybd_event(vk, scan_code, 0, 0);
 75     keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
 76     keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
 77     processPendingMessages();
 78 }
 79 
 80 static void simulate_typing_characters(HWND hwnd, const char* szChars)
 81 {
 82     int ret;
 83 
 84     while (*szChars != '\0') {
 85         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
 86         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
 87         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
 88         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
 89         szChars++;
 90     }
 91 }
 92 
 93 static const char haystack[] = "WINEWine wineWine wine WineWine";
 94                              /* ^0        ^10       ^20       ^30 */
 95 
 96 struct find_s {
 97   int start;
 98   int end;
 99   const char *needle;
100   int flags;
101   int expected_loc;
102   int _todo_wine;
103 };
104 
105 
106 struct find_s find_tests[] = {
107   /* Find in empty text */
108   {0, -1, "foo", FR_DOWN, -1, 0},
109   {0, -1, "foo", 0, -1, 0},
110   {0, -1, "", FR_DOWN, -1, 0},
111   {20, 5, "foo", FR_DOWN, -1, 0},
112   {5, 20, "foo", FR_DOWN, -1, 0}
113 };
114 
115 struct find_s find_tests2[] = {
116   /* No-result find */
117   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
118   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
119 
120   /* Subsequent finds */
121   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
122   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
123   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
124   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
125 
126   /* Find backwards */
127   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
128   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
129   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
130 
131   /* Case-insensitive */
132   {1, 31, "wInE", FR_DOWN, 4, 0},
133   {1, 31, "Wine", FR_DOWN, 4, 0},
134 
135   /* High-to-low ranges */
136   {20, 5, "Wine", FR_DOWN, -1, 0},
137   {2, 1, "Wine", FR_DOWN, -1, 0},
138   {30, 29, "Wine", FR_DOWN, -1, 0},
139   {20, 5, "Wine", 0, 13, 0},
140 
141   /* Find nothing */
142   {5, 10, "", FR_DOWN, -1, 0},
143   {10, 5, "", FR_DOWN, -1, 0},
144   {0, -1, "", FR_DOWN, -1, 0},
145   {10, 5, "", 0, -1, 0},
146 
147   /* Whole-word search */
148   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
149   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
150   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
151   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
152   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
153   {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
154   {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
155   
156   /* Bad ranges */
157   {5, 200, "XXX", FR_DOWN, -1, 0},
158   {-20, 20, "Wine", FR_DOWN, -1, 0},
159   {-20, 20, "Wine", FR_DOWN, -1, 0},
160   {-15, -20, "Wine", FR_DOWN, -1, 0},
161   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
162 
163   /* Check the case noted in bug 4479 where matches at end aren't recognized */
164   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
165   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
166   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
167   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
168   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
169 
170   /* The backwards case of bug 4479; bounds look right
171    * Fails because backward find is wrong */
172   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
173   {0, 20, "WINE", FR_MATCHCASE, -1, 0},
174 
175   {0, -1, "wineWine wine", 0, -1, 0},
176 };
177 
178 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
179   int findloc;
180   FINDTEXT ft;
181   memset(&ft, 0, sizeof(ft));
182   ft.chrg.cpMin = f->start;
183   ft.chrg.cpMax = f->end;
184   ft.lpstrText = f->needle;
185   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
186   ok(findloc == f->expected_loc,
187      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
188      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
189 }
190 
191 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
192     int id) {
193   int findloc;
194   FINDTEXTEX ft;
195   int expected_end_loc;
196 
197   memset(&ft, 0, sizeof(ft));
198   ft.chrg.cpMin = f->start;
199   ft.chrg.cpMax = f->end;
200   ft.lpstrText = f->needle;
201   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
202   ok(findloc == f->expected_loc,
203       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
204       name, id, f->needle, f->start, f->end, f->flags, findloc);
205   ok(ft.chrgText.cpMin == f->expected_loc,
206       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
207       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
208   expected_end_loc = ((f->expected_loc == -1) ? -1
209         : f->expected_loc + strlen(f->needle));
210   ok(ft.chrgText.cpMax == expected_end_loc,
211       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
212       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
213 }
214 
215 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
216     int num_tests)
217 {
218   int i;
219 
220   for (i = 0; i < num_tests; i++) {
221     if (find[i]._todo_wine) {
222       todo_wine {
223         check_EM_FINDTEXT(hwnd, name, &find[i], i);
224         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
225       }
226     } else {
227         check_EM_FINDTEXT(hwnd, name, &find[i], i);
228         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
229     }
230   }
231 }
232 
233 static void test_EM_FINDTEXT(void)
234 {
235   HWND hwndRichEdit = new_richedit(NULL);
236   CHARFORMAT2 cf2;
237 
238   /* Empty rich edit control */
239   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
240       sizeof(find_tests)/sizeof(struct find_s));
241 
242   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
243 
244   /* Haystack text */
245   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
246       sizeof(find_tests2)/sizeof(struct find_s));
247 
248   /* Setting a format on an arbitrary range should have no effect in search
249      results. This tests correct offset reporting across runs. */
250   cf2.cbSize = sizeof(CHARFORMAT2);
251   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
252              (LPARAM) &cf2);
253   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
254   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
255   SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
256   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
257 
258   /* Haystack text, again */
259   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
260       sizeof(find_tests2)/sizeof(struct find_s));
261 
262   /* Yet another range */
263   cf2.dwMask = CFM_BOLD | cf2.dwMask;
264   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
265   SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
266   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
267 
268   /* Haystack text, again */
269   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
270       sizeof(find_tests2)/sizeof(struct find_s));
271 
272   DestroyWindow(hwndRichEdit);
273 }
274 
275 static const struct getline_s {
276   int line;
277   size_t buffer_len;
278   const char *text;
279 } gl[] = {
280   {0, 10, "foo bar\r"},
281   {1, 10, "\r"},
282   {2, 10, "bar\r"},
283   {3, 10, "\r"},
284 
285   /* Buffer smaller than line length */
286   {0, 2, "foo bar\r"},
287   {0, 1, "foo bar\r"},
288   {0, 0, "foo bar\r"}
289 };
290 
291 static void test_EM_GETLINE(void)
292 {
293   int i;
294   HWND hwndRichEdit = new_richedit(NULL);
295   static const int nBuf = 1024;
296   char dest[1024], origdest[1024];
297   const char text[] = "foo bar\n"
298       "\n"
299       "bar\n";
300 
301   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
302 
303   memset(origdest, 0xBB, nBuf);
304   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
305   {
306     int nCopied;
307     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
308     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
309     memset(dest, 0xBB, nBuf);
310     *(WORD *) dest = gl[i].buffer_len;
311 
312     /* EM_GETLINE appends a "\r\0" to the end of the line
313      * nCopied counts up to and including the '\r' */
314     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
315     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
316        expected_nCopied);
317     /* two special cases since a parameter is passed via dest */
318     if (gl[i].buffer_len == 0)
319       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
320          "buffer_len=0\n");
321     else if (gl[i].buffer_len == 1)
322       ok(dest[0] == gl[i].text[0] && !dest[1] &&
323          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
324     else
325     {
326       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
327          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
328       ok(!strncmp(dest + expected_bytes_written, origdest
329                   + expected_bytes_written, nBuf - expected_bytes_written),
330          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
331     }
332   }
333 
334   DestroyWindow(hwndRichEdit);
335 }
336 
337 static void test_EM_LINELENGTH(void)
338 {
339   HWND hwndRichEdit = new_richedit(NULL);
340   const char * text =
341         "richedit1\r"
342         "richedit1\n"
343         "richedit1\r\n"
344         "richedit1";
345   int offset_test[10][2] = {
346         {0, 9},
347         {5, 9},
348         {10, 9},
349         {15, 9},
350         {20, 9},
351         {25, 9},
352         {30, 9},
353         {35, 9},
354         {40, 0},
355         {45, 0},
356   };
357   int i;
358   LRESULT result;
359 
360   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
361 
362   for (i = 0; i < 10; i++) {
363     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
364     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
365         offset_test[i][0], result, offset_test[i][1]);
366   }
367 
368   DestroyWindow(hwndRichEdit);
369 }
370 
371 static int get_scroll_pos_y(HWND hwnd)
372 {
373   POINT p = {-1, -1};
374   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
375   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
376   return p.y;
377 }
378 
379 static void move_cursor(HWND hwnd, long charindex)
380 {
381   CHARRANGE cr;
382   cr.cpMax = charindex;
383   cr.cpMin = charindex;
384   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
385 }
386 
387 static void line_scroll(HWND hwnd, int amount)
388 {
389   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
390 }
391 
392 static void test_EM_SCROLLCARET(void)
393 {
394   int prevY, curY;
395   HWND hwndRichEdit = new_richedit(NULL);
396   const char text[] = "aa\n"
397       "this is a long line of text that should be longer than the "
398       "control's width\n"
399       "cc\n"
400       "dd\n"
401       "ee\n"
402       "ff\n"
403       "gg\n"
404       "hh\n";
405 
406   /* Can't verify this */
407   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
408 
409   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
410 
411   /* Caret above visible window */
412   line_scroll(hwndRichEdit, 3);
413   prevY = get_scroll_pos_y(hwndRichEdit);
414   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
415   curY = get_scroll_pos_y(hwndRichEdit);
416   ok(prevY != curY, "%d == %d\n", prevY, curY);
417 
418   /* Caret below visible window */
419   move_cursor(hwndRichEdit, sizeof(text) - 1);
420   line_scroll(hwndRichEdit, -3);
421   prevY = get_scroll_pos_y(hwndRichEdit);
422   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
423   curY = get_scroll_pos_y(hwndRichEdit);
424   ok(prevY != curY, "%d == %d\n", prevY, curY);
425 
426   /* Caret in visible window */
427   move_cursor(hwndRichEdit, sizeof(text) - 2);
428   prevY = get_scroll_pos_y(hwndRichEdit);
429   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
430   curY = get_scroll_pos_y(hwndRichEdit);
431   ok(prevY == curY, "%d != %d\n", prevY, curY);
432 
433   /* Caret still in visible window */
434   line_scroll(hwndRichEdit, -1);
435   prevY = get_scroll_pos_y(hwndRichEdit);
436   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
437   curY = get_scroll_pos_y(hwndRichEdit);
438   ok(prevY == curY, "%d != %d\n", prevY, curY);
439 
440   DestroyWindow(hwndRichEdit);
441 }
442 
443 static void test_EM_POSFROMCHAR(void)
444 {
445   HWND hwndRichEdit = new_richedit(NULL);
446   int i;
447   LRESULT result;
448   unsigned int height = 0;
449   int xpos = 0;
450   static const char text[] = "aa\n"
451       "this is a long line of text that should be longer than the "
452       "control's width\n"
453       "cc\n"
454       "dd\n"
455       "ee\n"
456       "ff\n"
457       "gg\n"
458       "hh\n";
459 
460   /* Fill the control to lines to ensure that most of them are offscreen */
461   for (i = 0; i < 50; i++)
462   {
463     /* Do not modify the string; it is exactly 16 characters long. */
464     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
465     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
466   }
467 
468   /*
469    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
470    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
471    Richedit 3.0 accepts either of the above API conventions.
472    */
473 
474   /* Testing Richedit 2.0 API format */
475 
476   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
477      Since all lines are identical and drawn with the same font,
478      they should have the same height... right?
479    */
480   for (i = 0; i < 50; i++)
481   {
482     /* All the lines are 16 characters long */
483     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
484     if (i == 0)
485     {
486       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
487       todo_wine {
488       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
489       }
490       xpos = LOWORD(result);
491     }
492     else if (i == 1)
493     {
494       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
495       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
496       height = HIWORD(result);
497     }
498     else
499     {
500       ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
501       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
502     }
503   }
504 
505   /* Testing position at end of text */
506   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
507   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
508   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
509 
510   /* Testing position way past end of text */
511   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
512   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
513   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
514 
515   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
516   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
517   for (i = 0; i < 50; i++)
518   {
519     /* All the lines are 16 characters long */
520     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
521     ok((signed short)(HIWORD(result)) == (i - 1) * height,
522         "EM_POSFROMCHAR reports y=%hd, expected %d\n",
523         (signed short)(HIWORD(result)), (i - 1) * height);
524     ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
525   }
526 
527   /* Testing position at end of text */
528   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
529   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
530   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
531 
532   /* Testing position way past end of text */
533   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
534   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
535   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
536 
537   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
538   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
539   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
540 
541   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
542   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
543   todo_wine {
544   ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
545   }
546   xpos = LOWORD(result);
547 
548   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
549   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
550   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
551   todo_wine {
552   /* Fails on builtin because horizontal scrollbar is not being shown */
553   ok((signed short)(LOWORD(result)) < xpos,
554         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
555         (signed short)(LOWORD(result)), xpos);
556   }
557   DestroyWindow(hwndRichEdit);
558 }
559 
560 static void test_EM_SETCHARFORMAT(void)
561 {
562   HWND hwndRichEdit = new_richedit(NULL);
563   CHARFORMAT2 cf2;
564   int rc = 0;
565   int tested_effects[] = {
566     CFE_BOLD,
567     CFE_ITALIC,
568     CFE_UNDERLINE,
569     CFE_STRIKEOUT,
570     CFE_PROTECTED,
571     CFE_LINK,
572     CFE_SUBSCRIPT,
573     CFE_SUPERSCRIPT,
574     0
575   };
576   int i;
577   CHARRANGE cr;
578 
579   /* Invalid flags, CHARFORMAT2 structure blanked out */
580   memset(&cf2, 0, sizeof(cf2));
581   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
582              (LPARAM) &cf2);
583   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
584 
585   /* A valid flag, CHARFORMAT2 structure blanked out */
586   memset(&cf2, 0, sizeof(cf2));
587   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
588              (LPARAM) &cf2);
589   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
590 
591   /* A valid flag, CHARFORMAT2 structure blanked out */
592   memset(&cf2, 0, sizeof(cf2));
593   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
594              (LPARAM) &cf2);
595   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
596 
597   /* A valid flag, CHARFORMAT2 structure blanked out */
598   memset(&cf2, 0, sizeof(cf2));
599   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600              (LPARAM) &cf2);
601   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
602 
603   /* A valid flag, CHARFORMAT2 structure blanked out */
604   memset(&cf2, 0, sizeof(cf2));
605   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
606              (LPARAM) &cf2);
607   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
608 
609   /* Invalid flags, CHARFORMAT2 structure minimally filled */
610   memset(&cf2, 0, sizeof(cf2));
611   cf2.cbSize = sizeof(CHARFORMAT2);
612   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
613              (LPARAM) &cf2);
614   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
615   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
616   ok(rc == FALSE, "Should not be able to undo here.\n");
617   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
618 
619   /* A valid flag, CHARFORMAT2 structure minimally filled */
620   memset(&cf2, 0, sizeof(cf2));
621   cf2.cbSize = sizeof(CHARFORMAT2);
622   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623              (LPARAM) &cf2);
624   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
625   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
626   ok(rc == FALSE, "Should not be able to undo here.\n");
627   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
628 
629   /* A valid flag, CHARFORMAT2 structure minimally filled */
630   memset(&cf2, 0, sizeof(cf2));
631   cf2.cbSize = sizeof(CHARFORMAT2);
632   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
633              (LPARAM) &cf2);
634   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
635   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
636   ok(rc == FALSE, "Should not be able to undo here.\n");
637   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
638 
639   /* A valid flag, CHARFORMAT2 structure minimally filled */
640   memset(&cf2, 0, sizeof(cf2));
641   cf2.cbSize = sizeof(CHARFORMAT2);
642   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
643              (LPARAM) &cf2);
644   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
645   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
646   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
647   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
648 
649   /* A valid flag, CHARFORMAT2 structure minimally filled */
650   memset(&cf2, 0, sizeof(cf2));
651   cf2.cbSize = sizeof(CHARFORMAT2);
652   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
653              (LPARAM) &cf2);
654   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
656   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
657   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
658 
659   cf2.cbSize = sizeof(CHARFORMAT2);
660   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
661              (LPARAM) &cf2);
662 
663   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
664   cf2.cbSize = sizeof(CHARFORMAT2);
665   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
666              (LPARAM) &cf2);
667   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
668   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
669 
670   /* wParam==0 is default char format, does not set modify */
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, 0, (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   /* wParam==SCF_SELECTION sets modify if nonempty selection */
680   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
681   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
682   ok(rc == 0, "Text marked as modified, expected not modified!\n");
683   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
684   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
685   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
686   ok(rc == 0, "Text marked as modified, expected not modified!\n");
687 
688   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
689   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690   ok(rc == 0, "Text marked as modified, expected not modified!\n");
691   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
692   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
693   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
694   ok(rc == 0, "Text marked as modified, expected not modified!\n");
695   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
696   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (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   /* wParam==SCF_ALL sets modify regardless of whether text is present */
702   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
703   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
704   ok(rc == 0, "Text marked as modified, expected not modified!\n");
705   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
706   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
707   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
708   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
709 
710   DestroyWindow(hwndRichEdit);
711 
712   /* EM_GETCHARFORMAT tests */
713   for (i = 0; tested_effects[i]; i++)
714   {
715     hwndRichEdit = new_richedit(NULL);
716     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
717 
718     /* Need to set a TrueType font to get consistent CFM_BOLD results */
719     memset(&cf2, 0, sizeof(CHARFORMAT2));
720     cf2.cbSize = sizeof(CHARFORMAT2);
721     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
722     cf2.dwEffects = 0;
723     strcpy(cf2.szFaceName, "Courier New");
724     cf2.wWeight = FW_DONTCARE;
725     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
726 
727     memset(&cf2, 0, sizeof(CHARFORMAT2));
728     cf2.cbSize = sizeof(CHARFORMAT2);
729     SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
730     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
731     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
732           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
733           ||
734           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
735         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
736     ok((cf2.dwEffects & tested_effects[i]) == 0,
737         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
738 
739     memset(&cf2, 0, sizeof(CHARFORMAT2));
740     cf2.cbSize = sizeof(CHARFORMAT2);
741     cf2.dwMask = tested_effects[i];
742     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
743       cf2.dwMask = CFM_SUPERSCRIPT;
744     cf2.dwEffects = tested_effects[i];
745     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
746     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
747 
748     memset(&cf2, 0, sizeof(CHARFORMAT2));
749     cf2.cbSize = sizeof(CHARFORMAT2);
750     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
751     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
752     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
753           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
754           ||
755           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
756         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
757     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
758         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
759 
760     memset(&cf2, 0, sizeof(CHARFORMAT2));
761     cf2.cbSize = sizeof(CHARFORMAT2);
762     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
764     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
765           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
766           ||
767           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
768         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
769     ok((cf2.dwEffects & tested_effects[i]) == 0,
770         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
771 
772     memset(&cf2, 0, sizeof(CHARFORMAT2));
773     cf2.cbSize = sizeof(CHARFORMAT2);
774     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
775     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
776     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
777           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
778           ||
779           (cf2.dwMask & tested_effects[i]) == 0),
780         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
781 
782     DestroyWindow(hwndRichEdit);
783   }
784 
785   for (i = 0; tested_effects[i]; i++)
786   {
787     hwndRichEdit = new_richedit(NULL);
788     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789 
790     /* Need to set a TrueType font to get consistent CFM_BOLD results */
791     memset(&cf2, 0, sizeof(CHARFORMAT2));
792     cf2.cbSize = sizeof(CHARFORMAT2);
793     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
794     cf2.dwEffects = 0;
795     strcpy(cf2.szFaceName, "Courier New");
796     cf2.wWeight = FW_DONTCARE;
797     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
798 
799     memset(&cf2, 0, sizeof(CHARFORMAT2));
800     cf2.cbSize = sizeof(CHARFORMAT2);
801     cf2.dwMask = tested_effects[i];
802     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
803       cf2.dwMask = CFM_SUPERSCRIPT;
804     cf2.dwEffects = tested_effects[i];
805     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
806     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
807 
808     memset(&cf2, 0, sizeof(CHARFORMAT2));
809     cf2.cbSize = sizeof(CHARFORMAT2);
810     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
811     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
812     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
813           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
814           ||
815           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
816         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
817     ok((cf2.dwEffects & tested_effects[i]) == 0,
818         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
819 
820     memset(&cf2, 0, sizeof(CHARFORMAT2));
821     cf2.cbSize = sizeof(CHARFORMAT2);
822     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
823     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
824     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
825           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
826           ||
827           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
828         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
829     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
830         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
831 
832     memset(&cf2, 0, sizeof(CHARFORMAT2));
833     cf2.cbSize = sizeof(CHARFORMAT2);
834     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
835     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
836     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
837           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
838           ||
839           (cf2.dwMask & tested_effects[i]) == 0),
840         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
841     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
842         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
843 
844     DestroyWindow(hwndRichEdit);
845   }
846 
847   /* Effects applied on an empty selection should take effect when selection is
848      replaced with text */
849   hwndRichEdit = new_richedit(NULL);
850   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
851   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
852 
853   memset(&cf2, 0, sizeof(CHARFORMAT2));
854   cf2.cbSize = sizeof(CHARFORMAT2);
855   cf2.dwMask = CFM_BOLD;
856   cf2.dwEffects = CFE_BOLD;
857   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
858 
859   /* Selection is now nonempty */
860   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
861 
862   memset(&cf2, 0, sizeof(CHARFORMAT2));
863   cf2.cbSize = sizeof(CHARFORMAT2);
864   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
865   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 
867   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
868       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
869   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
870       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
871 
872 
873   /* Set two effects on an empty selection */
874   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
875   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
876 
877   memset(&cf2, 0, sizeof(CHARFORMAT2));
878   cf2.cbSize = sizeof(CHARFORMAT2);
879   cf2.dwMask = CFM_BOLD;
880   cf2.dwEffects = CFE_BOLD;
881   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
882   cf2.dwMask = CFM_ITALIC;
883   cf2.dwEffects = CFE_ITALIC;
884   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
885 
886   /* Selection is now nonempty */
887   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
888 
889   memset(&cf2, 0, sizeof(CHARFORMAT2));
890   cf2.cbSize = sizeof(CHARFORMAT2);
891   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
892   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
893 
894   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
895       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
896   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
897       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
898 
899   /* Setting the (empty) selection to exactly the same place as before should
900      NOT clear the insertion style! */
901   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
902   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
903 
904   memset(&cf2, 0, sizeof(CHARFORMAT2));
905   cf2.cbSize = sizeof(CHARFORMAT2);
906   cf2.dwMask = CFM_BOLD;
907   cf2.dwEffects = CFE_BOLD;
908   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
909 
910   /* Empty selection in same place, insert style should NOT be forgotten here. */
911   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
912 
913   /* Selection is now nonempty */
914   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
915 
916   memset(&cf2, 0, sizeof(CHARFORMAT2));
917   cf2.cbSize = sizeof(CHARFORMAT2);
918   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
919   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
920 
921   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
922       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
923   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
924       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
925 
926   /* Ditto with EM_EXSETSEL */
927   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
928   cr.cpMin = 2; cr.cpMax = 2;
929   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
930 
931   memset(&cf2, 0, sizeof(CHARFORMAT2));
932   cf2.cbSize = sizeof(CHARFORMAT2);
933   cf2.dwMask = CFM_BOLD;
934   cf2.dwEffects = CFE_BOLD;
935   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936 
937   /* Empty selection in same place, insert style should NOT be forgotten here. */
938   cr.cpMin = 2; cr.cpMax = 2;
939   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
940 
941   /* Selection is now nonempty */
942   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
943 
944   memset(&cf2, 0, sizeof(CHARFORMAT2));
945   cf2.cbSize = sizeof(CHARFORMAT2);
946   cr.cpMin = 2; cr.cpMax = 6;
947   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
948   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
949 
950   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
951       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
952   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
953       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
954 
955   DestroyWindow(hwndRichEdit);
956 }
957 
958 static void test_EM_SETTEXTMODE(void)
959 {
960   HWND hwndRichEdit = new_richedit(NULL);
961   CHARFORMAT2 cf2, cf2test;
962   CHARRANGE cr;
963   int rc = 0;
964 
965   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
966   /*Insert text into the control*/
967 
968   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
969 
970   /*Attempt to change the control to plain text mode*/
971   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
972   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
973 
974   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
975   If rich text is pasted, it should have the same formatting as the rest
976   of the text in the control*/
977 
978   /*Italicize the text
979   *NOTE: If the default text was already italicized, the test will simply
980   reverse; in other words, it will copy a regular "wine" into a plain
981   text window that uses an italicized format*/
982   cf2.cbSize = sizeof(CHARFORMAT2);
983   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
984              (LPARAM) &cf2);
985 
986   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
987   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
988 
989   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
990   ok(rc == 0, "Text marked as modified, expected not modified!\n");
991 
992   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
993   however, SCF_ALL has been implemented*/
994   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
995   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
996 
997   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
998   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
999 
1000   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1001 
1002   /*Select the string "wine"*/
1003   cr.cpMin = 0;
1004   cr.cpMax = 4;
1005   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1006 
1007   /*Copy the italicized "wine" to the clipboard*/
1008   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1009 
1010   /*Reset the formatting to default*/
1011   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1012   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1013   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1014 
1015   /*Clear the text in the control*/
1016   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1017 
1018   /*Switch to Plain Text Mode*/
1019   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1020   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1021 
1022   /*Input "wine" again in normal format*/
1023   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1024 
1025   /*Paste the italicized "wine" into the control*/
1026   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1027 
1028   /*Select a character from the first "wine" string*/
1029   cr.cpMin = 2;
1030   cr.cpMax = 3;
1031   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1032 
1033   /*Retrieve its formatting*/
1034   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1035               (LPARAM) &cf2);
1036 
1037   /*Select a character from the second "wine" string*/
1038   cr.cpMin = 5;
1039   cr.cpMax = 6;
1040   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1041 
1042   /*Retrieve its formatting*/
1043   cf2test.cbSize = sizeof(CHARFORMAT2);
1044   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1045                (LPARAM) &cf2test);
1046 
1047   /*Compare the two formattings*/
1048     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1049       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1050        cf2.dwEffects, cf2test.dwEffects);
1051   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1052                          printing "wine" in the current format(normal)
1053                          pasting "wine" from the clipboard(italicized)
1054                          comparing the two formats(should differ)*/
1055 
1056   /*Attempt to switch with text in control*/
1057   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1058   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1059 
1060   /*Clear control*/
1061   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1062 
1063   /*Switch into Rich Text mode*/
1064   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1065   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1066 
1067   /*Print "wine" in normal formatting into the control*/
1068   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1069 
1070   /*Paste italicized "wine" into the control*/
1071   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1072 
1073   /*Select text from the first "wine" string*/
1074   cr.cpMin = 1;
1075   cr.cpMax = 3;
1076   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1077 
1078   /*Retrieve its formatting*/
1079   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1080                 (LPARAM) &cf2);
1081 
1082   /*Select text from the second "wine" string*/
1083   cr.cpMin = 6;
1084   cr.cpMax = 7;
1085   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1086 
1087   /*Retrieve its formatting*/
1088   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1089                 (LPARAM) &cf2test);
1090 
1091   /*Test that the two formattings are not the same*/
1092   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1093       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1094       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1095 
1096   DestroyWindow(hwndRichEdit);
1097 }
1098 
1099 static void test_SETPARAFORMAT(void)
1100 {
1101   HWND hwndRichEdit = new_richedit(NULL);
1102   PARAFORMAT2 fmt;
1103   HRESULT ret;
1104   fmt.cbSize = sizeof(PARAFORMAT2);
1105   fmt.dwMask = PFM_ALIGNMENT;
1106   fmt.wAlignment = PFA_LEFT;
1107 
1108   ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1109   ok(ret != 0, "expected non-zero got %d\n", ret);
1110 
1111   fmt.cbSize = sizeof(PARAFORMAT2);
1112   fmt.dwMask = -1;
1113   ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1114   ok(ret == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, ret);
1115   ok(fmt.dwMask == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, fmt.dwMask);
1116 
1117   DestroyWindow(hwndRichEdit);
1118 }
1119 
1120 static void test_TM_PLAINTEXT(void)
1121 {
1122   /*Tests plain text properties*/
1123 
1124   HWND hwndRichEdit = new_richedit(NULL);
1125   CHARFORMAT2 cf2, cf2test;
1126   CHARRANGE cr;
1127   int rc = 0;
1128 
1129   /*Switch to plain text mode*/
1130 
1131   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1132   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1133 
1134   /*Fill control with text*/
1135 
1136   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1137 
1138   /*Select some text and bold it*/
1139 
1140   cr.cpMin = 10;
1141   cr.cpMax = 20;
1142   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1143   cf2.cbSize = sizeof(CHARFORMAT2);
1144   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1145               (LPARAM) &cf2);
1146 
1147   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1148   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1149 
1150   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1151   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1152 
1153   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1154   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1155 
1156   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1157   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1158 
1159   /*Get the formatting of those characters*/
1160 
1161   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1162 
1163   /*Get the formatting of some other characters*/
1164   cf2test.cbSize = sizeof(CHARFORMAT2);
1165   cr.cpMin = 21;
1166   cr.cpMax = 30;
1167   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1168   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1169 
1170   /*Test that they are the same as plain text allows only one formatting*/
1171 
1172   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1173      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1174      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1175   
1176   /*Fill the control with a "wine" string, which when inserted will be bold*/
1177 
1178   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1179 
1180   /*Copy the bolded "wine" string*/
1181 
1182   cr.cpMin = 0;
1183   cr.cpMax = 4;
1184   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1185   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1186 
1187   /*Swap back to rich text*/
1188 
1189   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1190   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1191 
1192   /*Set the default formatting to bold italics*/
1193 
1194   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1195   cf2.dwMask |= CFM_ITALIC;
1196   cf2.dwEffects ^= CFE_ITALIC;
1197   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1198   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1199 
1200   /*Set the text in the control to "wine", which will be bold and italicized*/
1201 
1202   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1203 
1204   /*Paste the plain text "wine" string, which should take the insert
1205    formatting, which at the moment is bold italics*/
1206 
1207   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1208 
1209   /*Select the first "wine" string and retrieve its formatting*/
1210 
1211   cr.cpMin = 1;
1212   cr.cpMax = 3;
1213   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1214   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1215 
1216   /*Select the second "wine" string and retrieve its formatting*/
1217 
1218   cr.cpMin = 5;
1219   cr.cpMax = 7;
1220   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1221   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1222 
1223   /*Compare the two formattings. They should be the same.*/
1224 
1225   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1226      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1227      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1228   DestroyWindow(hwndRichEdit);
1229 }
1230 
1231 static void test_WM_GETTEXT(void)
1232 {
1233     HWND hwndRichEdit = new_richedit(NULL);
1234     static const char text[] = "Hello. My name is RichEdit!";
1235     static const char text2[] = "Hello. My name is RichEdit!\r";
1236     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1237     char buffer[1024] = {0};
1238     int result;
1239 
1240     /* Baseline test with normal-sized buffer */
1241     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1242     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1243     ok(result == lstrlen(buffer),
1244         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1245     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1246     result = strcmp(buffer,text);
1247     ok(result == 0, 
1248         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1249 
1250     /* Test for returned value of WM_GETTEXTLENGTH */
1251     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1252     ok(result == lstrlen(text),
1253         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1254         result, lstrlen(text));
1255 
1256     /* Test for behavior in overflow case */
1257     memset(buffer, 0, 1024);
1258     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1259     ok(result == 0 ||
1260        result == lstrlenA(text) - 1, /* XP, win2k3 */
1261         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1262     result = strcmp(buffer,text);
1263     if (result)
1264         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1265     ok(result == 0,
1266         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1267 
1268     /* Baseline test with normal-sized buffer and carriage return */
1269     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1270     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1271     ok(result == lstrlen(buffer),
1272         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1273     result = strcmp(buffer,text2_after);
1274     ok(result == 0,
1275         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1276 
1277     /* Test for returned value of WM_GETTEXTLENGTH */
1278     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1279     ok(result == lstrlen(text2_after),
1280         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1281         result, lstrlen(text2_after));
1282 
1283     /* Test for behavior of CRLF conversion in case of overflow */
1284     memset(buffer, 0, 1024);
1285     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1286     ok(result == 0 ||
1287        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1288         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1289     result = strcmp(buffer,text2);
1290     if (result)
1291         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1292     ok(result == 0,
1293         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1294 
1295     DestroyWindow(hwndRichEdit);
1296 }
1297 
1298 static void test_EM_GETTEXTRANGE(void)
1299 {
1300     HWND hwndRichEdit = new_richedit(NULL);
1301     const char * text1 = "foo bar\r\nfoo bar";
1302     const char * text2 = "foo bar\rfoo bar";
1303     const char * expect = "bar\rfoo";
1304     char buffer[1024] = {0};
1305     LRESULT result;
1306     TEXTRANGEA textRange;
1307 
1308     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
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     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1318 
1319     textRange.lpstrText = buffer;
1320     textRange.chrg.cpMin = 4;
1321     textRange.chrg.cpMax = 11;
1322     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1323     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1324     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1325 
1326     DestroyWindow(hwndRichEdit);
1327 }
1328 
1329 static void test_EM_GETSELTEXT(void)
1330 {
1331     HWND hwndRichEdit = new_richedit(NULL);
1332     const char * text1 = "foo bar\r\nfoo bar";
1333     const char * text2 = "foo bar\rfoo bar";
1334     const char * expect = "bar\rfoo";
1335     char buffer[1024] = {0};
1336     LRESULT result;
1337 
1338     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1339 
1340     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1341     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1342     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1343     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1344 
1345     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1346 
1347     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1348     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1349     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1350     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1351 
1352     DestroyWindow(hwndRichEdit);
1353 }
1354 
1355 /* FIXME: need to test unimplemented options and robustly test wparam */
1356 static void test_EM_SETOPTIONS(void)
1357 {
1358     HWND hwndRichEdit = new_richedit(NULL);
1359     static const char text[] = "Hello. My name is RichEdit!";
1360     char buffer[1024] = {0};
1361 
1362     /* NEGATIVE TESTING - NO OPTIONS SET */
1363     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1364     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1365 
1366     /* testing no readonly by sending 'a' to the control*/
1367     SetFocus(hwndRichEdit);
1368     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1369     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1370     ok(buffer[0]=='a', 
1371        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1372     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1373 
1374     /* READONLY - sending 'a' to the control */
1375     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1376     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1377     SetFocus(hwndRichEdit);
1378     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1379     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1380     ok(buffer[0]==text[0], 
1381        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1382 
1383     DestroyWindow(hwndRichEdit);
1384 }
1385 
1386 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1387 {
1388   CHARFORMAT2W text_format;
1389   text_format.cbSize = sizeof(text_format);
1390   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1391   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1392   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1393 }
1394 
1395 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1396 {
1397   int link_present = 0;
1398 
1399   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1400   if (is_url) 
1401   { /* control text is url; should get CFE_LINK */
1402         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1403   }
1404   else 
1405   {
1406     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1407   }
1408 }
1409 
1410 static HWND new_static_wnd(HWND parent) {
1411   return new_window("Static", 0, parent);
1412 }
1413 
1414 static void test_EM_AUTOURLDETECT(void)
1415 {
1416   /* DO NOT change the properties of the first two elements. To shorten the
1417      tests, all tests after WM_SETTEXT test just the first two elements -
1418      one non-URL and one URL */
1419   struct urls_s {
1420     const char *text;
1421     int is_url;
1422   } urls[12] = {
1423     {"winehq.org", 0},
1424     {"http://www.winehq.org", 1},
1425     {"http//winehq.org", 0},
1426     {"ww.winehq.org", 0},
1427     {"www.winehq.org", 1},
1428     {"ftp://192.168.1.1", 1},
1429     {"ftp//192.168.1.1", 0},
1430     {"mailto:your@email.com", 1},    
1431     {"prospero:prosperoserver", 1},
1432     {"telnet:test", 1},
1433     {"news:newserver", 1},
1434     {"wais:waisserver", 1}  
1435   };
1436 
1437   int i, j;
1438   int urlRet=-1;
1439   HWND hwndRichEdit, parent;
1440 
1441   /* All of the following should cause the URL to be detected  */
1442   const char * templates_delim[] = {
1443     "This is some text with X on it",
1444     "This is some text with (X) on it",
1445     "This is some text with X\r on it",
1446     "This is some text with ---X--- on it",
1447     "This is some text with \"X\" on it",
1448     "This is some text with 'X' on it",
1449     "This is some text with 'X' on it",
1450     "This is some text with :X: on it",
1451 
1452     "This text ends with X",
1453 
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     "This is some text with X' on it",
1458     "This is some text with X: on it",
1459 
1460     "This is some text with (X on it",
1461     "This is some text with \rX 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   };
1467   /* None of these should cause the URL to be detected */
1468   const char * templates_non_delim[] = {
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     "This is some text with \\X\\ on it",
1477     "This is some text with |X on it",
1478     "This is some text with *X on it",
1479     "This is some text with /X on it",
1480     "This is some text with +X on it",
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   };
1486   /* All of these cause the URL detection to be extended by one more byte,
1487      thus demonstrating that the tested character is considered as part
1488      of the URL. */
1489   const char * templates_xten_delim[] = {
1490     "This is some text with X| on it",
1491     "This is some text with X* on it",
1492     "This is some text with X/ on it",
1493     "This is some text with X+ on it",
1494     "This is some text with X% on it",
1495     "This is some text with X# on it",
1496     "This is some text with X@ on it",
1497     "This is some text with X\\ on it",
1498   };
1499   char buffer[1024];
1500 
1501   parent = new_static_wnd(NULL);
1502   hwndRichEdit = new_richedit(parent);
1503   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1504   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1505   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1506   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1507   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1508   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1509   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1510   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1511   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1512   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1513   /* for each url, check the text to see if CFE_LINK effect is present */
1514   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1515 
1516     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1517     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1518     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1519 
1520     /* Link detection should happen immediately upon WM_SETTEXT */
1521     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1522     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1523     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1524   }
1525   DestroyWindow(hwndRichEdit);
1526 
1527   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1528   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1529     hwndRichEdit = new_richedit(parent);
1530 
1531     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1532       char * at_pos;
1533       int at_offset;
1534       int end_offset;
1535 
1536       at_pos = strchr(templates_delim[j], 'X');
1537       at_offset = at_pos - templates_delim[j];
1538       strncpy(buffer, templates_delim[j], at_offset);
1539       buffer[at_offset] = '\0';
1540       strcat(buffer, urls[i].text);
1541       strcat(buffer, templates_delim[j] + at_offset + 1);
1542       end_offset = at_offset + strlen(urls[i].text);
1543 
1544       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1545       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1546 
1547       /* This assumes no templates start with the URL itself, and that they
1548          have at least two characters before the URL text */
1549       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1550         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1551       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1552         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1553       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1554         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1555 
1556       if (urls[i].is_url)
1557       {
1558         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1559           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1560         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1561           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1562       }
1563       else
1564       {
1565         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1566           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1567         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1568           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1569       }
1570       if (buffer[end_offset] != '\0')
1571       {
1572         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1573           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1574         if (buffer[end_offset +1] != '\0')
1575         {
1576           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1577             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1578         }
1579       }
1580     }
1581 
1582     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1583       char * at_pos;
1584       int at_offset;
1585       int end_offset;
1586 
1587       at_pos = strchr(templates_non_delim[j], 'X');
1588       at_offset = at_pos - templates_non_delim[j];
1589       strncpy(buffer, templates_non_delim[j], at_offset);
1590       buffer[at_offset] = '\0';
1591       strcat(buffer, urls[i].text);
1592       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1593       end_offset = at_offset + strlen(urls[i].text);
1594 
1595       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1596       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1597 
1598       /* This assumes no templates start with the URL itself, and that they
1599          have at least two characters before the URL text */
1600       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1601         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1602       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1603         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1604       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1605         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1606 
1607       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1608         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1609       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1610         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1611       if (buffer[end_offset] != '\0')
1612       {
1613         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1614           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1615         if (buffer[end_offset +1] != '\0')
1616         {
1617           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1618             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1619         }
1620       }
1621     }
1622 
1623     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1624       char * at_pos;
1625       int at_offset;
1626       int end_offset;
1627 
1628       at_pos = strchr(templates_xten_delim[j], 'X');
1629       at_offset = at_pos - templates_xten_delim[j];
1630       strncpy(buffer, templates_xten_delim[j], at_offset);
1631       buffer[at_offset] = '\0';
1632       strcat(buffer, urls[i].text);
1633       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1634       end_offset = at_offset + strlen(urls[i].text);
1635 
1636       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1637       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1638 
1639       /* This assumes no templates start with the URL itself, and that they
1640          have at least two characters before the URL text */
1641       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1642         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1643       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1644         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1645       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1646         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1647 
1648       if (urls[i].is_url)
1649       {
1650         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1651           "CFE_LINK not 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 not 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 not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1656       }
1657       else
1658       {
1659         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1660           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1661         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1662           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1663         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1664           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1665       }
1666       if (buffer[end_offset +1] != '\0')
1667       {
1668         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1669           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1670         if (buffer[end_offset +2] != '\0')
1671         {
1672           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1673             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1674         }
1675       }
1676     }
1677 
1678     DestroyWindow(hwndRichEdit);
1679     hwndRichEdit = NULL;
1680   }
1681 
1682   /* Test detection of URLs within normal text - WM_CHAR case. */
1683   /* Test only the first two URL examples for brevity */
1684   for (i = 0; i < 2; i++) {
1685     hwndRichEdit = new_richedit(parent);
1686 
1687     /* Also for brevity, test only the first three delimiters */
1688     for (j = 0; j < 3; j++) {
1689       char * at_pos;
1690       int at_offset;
1691       int end_offset;
1692       int u, v;
1693 
1694       at_pos = strchr(templates_delim[j], 'X');
1695       at_offset = at_pos - templates_delim[j];
1696       end_offset = at_offset + strlen(urls[i].text);
1697 
1698       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1699       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1700       for (u = 0; templates_delim[j][u]; u++) {
1701         if (templates_delim[j][u] == '\r') {
1702           simulate_typing_characters(hwndRichEdit, "\r");
1703         } else if (templates_delim[j][u] != 'X') {
1704           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1705         } else {
1706           for (v = 0; urls[i].text[v]; v++) {
1707             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1708           }
1709         }
1710       }
1711       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1712 
1713       /* This assumes no templates start with the URL itself, and that they
1714          have at least two characters before the URL text */
1715       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1716         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1717       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1718         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1719       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1720         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1721 
1722       if (urls[i].is_url)
1723       {
1724         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1725           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1726         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1727           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1728       }
1729       else
1730       {
1731         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1732           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1733         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1734           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1735       }
1736       if (buffer[end_offset] != '\0')
1737       {
1738         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1739           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1740         if (buffer[end_offset +1] != '\0')
1741         {
1742           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1743             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1744         }
1745       }
1746 
1747       /* The following will insert a paragraph break after the first character
1748          of the URL candidate, thus breaking the URL. It is expected that the
1749          CFE_LINK attribute should break across both pieces of the URL */
1750       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1751       simulate_typing_characters(hwndRichEdit, "\r");
1752       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1753 
1754       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1755         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1756       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1757         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1758       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1759         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1760 
1761       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1762         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1763       /* end_offset moved because of paragraph break */
1764       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1765         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1766       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1767       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
1768       {
1769         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1770           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1771         if (buffer[end_offset +2] != '\0')
1772         {
1773           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1774             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1775         }
1776       }
1777 
1778       /* The following will remove the just-inserted paragraph break, thus
1779          restoring the URL */
1780       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1781       simulate_typing_characters(hwndRichEdit, "\b");
1782       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1783 
1784       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1785         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1786       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1787         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1788       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1789         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1790 
1791       if (urls[i].is_url)
1792       {
1793         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1794           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1795         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1796           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1797       }
1798       else
1799       {
1800         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1801           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1802         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1803           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1804       }
1805       if (buffer[end_offset] != '\0')
1806       {
1807         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1808           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1809         if (buffer[end_offset +1] != '\0')
1810         {
1811           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1812             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1813         }
1814       }
1815     }
1816     DestroyWindow(hwndRichEdit);
1817     hwndRichEdit = NULL;
1818   }
1819 
1820   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1821   /* Test just the first two URL examples for brevity */
1822   for (i = 0; i < 2; i++) {
1823     SETTEXTEX st;
1824 
1825     hwndRichEdit = new_richedit(parent);
1826 
1827     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1828        be detected:
1829        1) Set entire text, a la WM_SETTEXT
1830        2) Set a selection of the text to the URL
1831        3) Set a portion of the text at a time, which eventually results in
1832           an URL
1833        All of them should give equivalent results
1834      */
1835 
1836     /* Set entire text in one go, like WM_SETTEXT */
1837     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1838       char * at_pos;
1839       int at_offset;
1840       int end_offset;
1841 
1842       st.codepage = CP_ACP;
1843       st.flags = ST_DEFAULT;
1844 
1845       at_pos = strchr(templates_delim[j], 'X');
1846       at_offset = at_pos - templates_delim[j];
1847       strncpy(buffer, templates_delim[j], at_offset);
1848       buffer[at_offset] = '\0';
1849       strcat(buffer, urls[i].text);
1850       strcat(buffer, templates_delim[j] + at_offset + 1);
1851       end_offset = at_offset + strlen(urls[i].text);
1852 
1853       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1854       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1855 
1856       /* This assumes no templates start with the URL itself, and that they
1857          have at least two characters before the URL text */
1858       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1859         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1860       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1861         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1862       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1863         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1864 
1865       if (urls[i].is_url)
1866       {
1867         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1868           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1869         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1870           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1871       }
1872       else
1873       {
1874         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1875           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1876         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1877           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1878       }
1879       if (buffer[end_offset] != '\0')
1880       {
1881         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1882           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1883         if (buffer[end_offset +1] != '\0')
1884         {
1885           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1886             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1887         }
1888       }
1889     }
1890 
1891     /* Set selection with X to the URL */
1892     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1893       char * at_pos;
1894       int at_offset;
1895       int end_offset;
1896 
1897       at_pos = strchr(templates_delim[j], 'X');
1898       at_offset = at_pos - templates_delim[j];
1899       end_offset = at_offset + strlen(urls[i].text);
1900 
1901       st.codepage = CP_ACP;
1902       st.flags = ST_DEFAULT;
1903       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1904       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1905       st.flags = ST_SELECTION;
1906       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1907       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1908       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1909 
1910       /* This assumes no templates start with the URL itself, and that they
1911          have at least two characters before the URL text */
1912       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1913         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1914       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1915         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1916       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1917         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1918 
1919       if (urls[i].is_url)
1920       {
1921         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1922           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1923         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1924           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1925       }
1926       else
1927       {
1928         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1929           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1930         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1931           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1932       }
1933       if (buffer[end_offset] != '\0')
1934       {
1935         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1936           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1937         if (buffer[end_offset +1] != '\0')
1938         {
1939           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1940             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1941         }
1942       }
1943     }
1944 
1945     /* Set selection with X to the first character of the URL, then the rest */
1946     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1947       char * at_pos;
1948       int at_offset;
1949       int end_offset;
1950 
1951       at_pos = strchr(templates_delim[j], 'X');
1952       at_offset = at_pos - templates_delim[j];
1953       end_offset = at_offset + strlen(urls[i].text);
1954 
1955       strcpy(buffer, "YY");
1956       buffer[0] = urls[i].text[0];
1957 
1958       st.codepage = CP_ACP;
1959       st.flags = ST_DEFAULT;
1960       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1961       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1962       st.flags = ST_SELECTION;
1963       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1964       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1965       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1966       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1967       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1968 
1969       /* This assumes no templates start with the URL itself, and that they
1970          have at least two characters before the URL text */
1971       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1972         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1973       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1974         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1975       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1976         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1977 
1978       if (urls[i].is_url)
1979       {
1980         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1981           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1982         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1983           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1984       }
1985       else
1986       {
1987         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1988           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1989         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1990           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1991       }
1992       if (buffer[end_offset] != '\0')
1993       {
1994         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1995           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1996         if (buffer[end_offset +1] != '\0')
1997         {
1998           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1999             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2000         }
2001       }
2002     }
2003 
2004     DestroyWindow(hwndRichEdit);
2005     hwndRichEdit = NULL;
2006   }
2007 
2008   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2009   /* Test just the first two URL examples for brevity */
2010   for (i = 0; i < 2; i++) {
2011     hwndRichEdit = new_richedit(parent);
2012 
2013     /* Set selection with X to the URL */
2014     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2015       char * at_pos;
2016       int at_offset;
2017       int end_offset;
2018 
2019       at_pos = strchr(templates_delim[j], 'X');
2020       at_offset = at_pos - templates_delim[j];
2021       end_offset = at_offset + strlen(urls[i].text);
2022 
2023       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2024       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2025       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2026       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2027       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2028 
2029       /* This assumes no templates start with the URL itself, and that they
2030          have at least two characters before the URL text */
2031       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2032         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2033       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2034         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2035       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2036         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2037 
2038       if (urls[i].is_url)
2039       {
2040         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2041           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2042         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2043           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2044       }
2045       else
2046       {
2047         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2049         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2050           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2051       }
2052       if (buffer[end_offset] != '\0')
2053       {
2054         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2055           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2056         if (buffer[end_offset +1] != '\0')
2057         {
2058           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2059             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2060         }
2061       }
2062     }
2063 
2064     /* Set selection with X to the first character of the URL, then the rest */
2065     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2066       char * at_pos;
2067       int at_offset;
2068       int end_offset;
2069 
2070       at_pos = strchr(templates_delim[j], 'X');
2071       at_offset = at_pos - templates_delim[j];
2072       end_offset = at_offset + strlen(urls[i].text);
2073 
2074       strcpy(buffer, "YY");
2075       buffer[0] = urls[i].text[0];
2076 
2077       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2078       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2079       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2080       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2081       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2082       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2083       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2084 
2085       /* This assumes no templates start with the URL itself, and that they
2086          have at least two characters before the URL text */
2087       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2088         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2089       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2090         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2091       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2092         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2093 
2094       if (urls[i].is_url)
2095       {
2096         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2097           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2098         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2099           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2100       }
2101       else
2102       {
2103         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2104           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2105         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2106           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2107       }
2108       if (buffer[end_offset] != '\0')
2109       {
2110         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2111           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2112         if (buffer[end_offset +1] != '\0')
2113         {
2114           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2115             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2116         }
2117       }
2118     }
2119 
2120     DestroyWindow(hwndRichEdit);
2121     hwndRichEdit = NULL;
2122   }
2123 
2124   DestroyWindow(parent);
2125 }
2126 
2127 static void test_EM_SCROLL(void)
2128 {
2129   int i, j;
2130   int r; /* return value */
2131   int expr; /* expected return value */
2132   HWND hwndRichEdit = new_richedit(NULL);
2133   int y_before, y_after; /* units of lines of text */
2134 
2135   /* test a richedit box containing a single line of text */
2136   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2137   expr = 0x00010000;
2138   for (i = 0; i < 4; i++) {
2139     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2140 
2141     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2142     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2143     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2144        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2145     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2146        "(i == %d)\n", y_after, i);
2147   }
2148 
2149   /*
2150    * test a richedit box that will scroll. There are two general
2151    * cases: the case without any long lines and the case with a long
2152    * line.
2153    */
2154   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2155     if (i == 0)
2156       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2157     else
2158       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2159                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2160                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2161                   "LONG LINE \nb\nc\nd\ne");
2162     for (j = 0; j < 12; j++) /* reset scroll position to top */
2163       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2164 
2165     /* get first visible line */
2166     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2167     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2168 
2169     /* get new current first visible line */
2170     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2171 
2172     ok(((r & 0xffffff00) == 0x00010000) &&
2173        ((r & 0x000000ff) != 0x00000000),
2174        "EM_SCROLL page down didn't scroll by a small positive number of "
2175        "lines (r == 0x%08x)\n", r);
2176     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2177        "(line %d scrolled to line %d\n", y_before, y_after);
2178 
2179     y_before = y_after;
2180     
2181     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2182     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2183     ok(((r & 0xffffff00) == 0x0001ff00),
2184        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2185        "(r == 0x%08x)\n", r);
2186     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2187        "%d scrolled to line %d\n", y_before, y_after);
2188     
2189     y_before = y_after;
2190 
2191     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2192 
2193     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2194 
2195     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2196        "(r == 0x%08x)\n", r);
2197     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2198        "1 line (%d scrolled to %d)\n", y_before, y_after);
2199 
2200     y_before = y_after;
2201 
2202     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2203 
2204     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2205 
2206     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2207        "(r == 0x%08x)\n", r);
2208     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2209        "line (%d scrolled to %d)\n", y_before, y_after);
2210 
2211     y_before = y_after;
2212 
2213     r = SendMessage(hwndRichEdit, EM_SCROLL,
2214                     SB_LINEUP, 0); /* lineup beyond top */
2215 
2216     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2217 
2218     ok(r == 0x00010000,
2219        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2220     ok(y_before == y_after,
2221        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2222 
2223     y_before = y_after;
2224 
2225     r = SendMessage(hwndRichEdit, EM_SCROLL,
2226                     SB_PAGEUP, 0);/*page up beyond top */
2227 
2228     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2229 
2230     ok(r == 0x00010000,
2231        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2232     ok(y_before == y_after,
2233        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2234 
2235     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2236       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2237     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2238     r = SendMessage(hwndRichEdit, EM_SCROLL,
2239                     SB_PAGEDOWN, 0); /* page down beyond bot */
2240     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2241 
2242     ok(r == 0x00010000,
2243        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2244     ok(y_before == y_after,
2245        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2246        y_before, y_after);
2247 
2248     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2249     SendMessage(hwndRichEdit, EM_SCROLL,
2250                 SB_LINEDOWN, 0); /* line down beyond bot */
2251     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2252     
2253     ok(r == 0x00010000,
2254        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2255     ok(y_before == y_after,
2256        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2257        y_before, y_after);
2258   }
2259   DestroyWindow(hwndRichEdit);
2260 }
2261 
2262 unsigned int recursionLevel = 0;
2263 unsigned int WM_SIZE_recursionLevel = 0;
2264 BOOL bailedOutOfRecursion = FALSE;
2265 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2266 
2267 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2268 {
2269     LRESULT r;
2270 
2271     if (bailedOutOfRecursion) return 0;
2272     if (recursionLevel >= 32) {
2273         bailedOutOfRecursion = TRUE;
2274         return 0;
2275     }
2276 
2277     recursionLevel++;
2278     switch (message) {
2279     case WM_SIZE:
2280         WM_SIZE_recursionLevel++;
2281         r = richeditProc(hwnd, message, wParam, lParam);
2282         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2283         ShowScrollBar(hwnd, SB_VERT, TRUE);
2284         WM_SIZE_recursionLevel--;
2285         break;
2286     default:
2287         r = richeditProc(hwnd, message, wParam, lParam);
2288         break;
2289     }
2290     recursionLevel--;
2291     return r;
2292 }
2293 
2294 static void test_scrollbar_visibility(void)
2295 {
2296   HWND hwndRichEdit;
2297   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2298   SCROLLINFO si;
2299   WNDCLASSA cls;
2300   BOOL r;
2301 
2302   /* These tests show that richedit should temporarily refrain from automatically
2303      hiding or showing its scrollbars (vertical at least) when an explicit request
2304      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2305      Some applications depend on forced showing (when otherwise richedit would
2306      hide the vertical scrollbar) and are thrown on an endless recursive loop
2307      if richedit auto-hides the scrollbar again. Apparently they never heard of
2308      the ES_DISABLENOSCROLL style... */
2309 
2310   hwndRichEdit = new_richedit(NULL);
2311 
2312   /* Test default scrollbar visibility behavior */
2313   memset(&si, 0, sizeof(si));
2314   si.cbSize = sizeof(si);
2315   si.fMask = SIF_PAGE | SIF_RANGE;
2316   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2317   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2318     "Vertical scrollbar is visible, should be invisible.\n");
2319   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2320         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2321         si.nPage, si.nMin, si.nMax);
2322 
2323   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2324   memset(&si, 0, sizeof(si));
2325   si.cbSize = sizeof(si);
2326   si.fMask = SIF_PAGE | SIF_RANGE;
2327   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2328   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2329     "Vertical scrollbar is visible, should be invisible.\n");
2330   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2331         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2332         si.nPage, si.nMin, si.nMax);
2333 
2334   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2335   memset(&si, 0, sizeof(si));
2336   si.cbSize = sizeof(si);
2337   si.fMask = SIF_PAGE | SIF_RANGE;
2338   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2339   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2340     "Vertical scrollbar is invisible, should be visible.\n");
2341   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2342         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2343         si.nPage, si.nMin, si.nMax);
2344 
2345   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2346      even though it hides the scrollbar */
2347   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2348   memset(&si, 0, sizeof(si));
2349   si.cbSize = sizeof(si);
2350   si.fMask = SIF_PAGE | SIF_RANGE;
2351   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2352   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2353     "Vertical scrollbar is visible, should be invisible.\n");
2354   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2355         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2356         si.nPage, si.nMin, si.nMax);
2357 
2358   /* Setting non-scrolling text again does *not* reset scrollbar range */
2359   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2360   memset(&si, 0, sizeof(si));
2361   si.cbSize = sizeof(si);
2362   si.fMask = SIF_PAGE | SIF_RANGE;
2363   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2364   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2365     "Vertical scrollbar is visible, should be invisible.\n");
2366   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2367         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2368         si.nPage, si.nMin, si.nMax);
2369 
2370   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2371   memset(&si, 0, sizeof(si));
2372   si.cbSize = sizeof(si);
2373   si.fMask = SIF_PAGE | SIF_RANGE;
2374   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2375   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2376     "Vertical scrollbar is visible, should be invisible.\n");
2377   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2378         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2379         si.nPage, si.nMin, si.nMax);
2380 
2381   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2382   memset(&si, 0, sizeof(si));
2383   si.cbSize = sizeof(si);
2384   si.fMask = SIF_PAGE | SIF_RANGE;
2385   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2386   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2387     "Vertical scrollbar is visible, should be invisible.\n");
2388   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2389         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2390         si.nPage, si.nMin, si.nMax);
2391 
2392   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2393   memset(&si, 0, sizeof(si));
2394   si.cbSize = sizeof(si);
2395   si.fMask = SIF_PAGE | SIF_RANGE;
2396   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2397   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2398     "Vertical scrollbar is visible, should be invisible.\n");
2399   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2400         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2401         si.nPage, si.nMin, si.nMax);
2402 
2403   DestroyWindow(hwndRichEdit);
2404 
2405   /* Test again, with ES_DISABLENOSCROLL style */
2406   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2407 
2408   /* Test default scrollbar visibility behavior */
2409   memset(&si, 0, sizeof(si));
2410   si.cbSize = sizeof(si);
2411   si.fMask = SIF_PAGE | SIF_RANGE;
2412   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2413   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2414     "Vertical scrollbar is invisible, should be visible.\n");
2415   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2416         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2417         si.nPage, si.nMin, si.nMax);
2418 
2419   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2420   memset(&si, 0, sizeof(si));
2421   si.cbSize = sizeof(si);
2422   si.fMask = SIF_PAGE | SIF_RANGE;
2423   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2424   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2425     "Vertical scrollbar is invisible, should be visible.\n");
2426   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2427         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2428         si.nPage, si.nMin, si.nMax);
2429 
2430   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2431   memset(&si, 0, sizeof(si));
2432   si.cbSize = sizeof(si);
2433   si.fMask = SIF_PAGE | SIF_RANGE;
2434   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2435   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2436     "Vertical scrollbar is invisible, should be visible.\n");
2437   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2438         "reported page/range is %d (%d..%d)\n",
2439         si.nPage, si.nMin, si.nMax);
2440 
2441   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2442   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2443   memset(&si, 0, sizeof(si));
2444   si.cbSize = sizeof(si);
2445   si.fMask = SIF_PAGE | SIF_RANGE;
2446   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2447   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2448     "Vertical scrollbar is invisible, should be visible.\n");
2449   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2450         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2451         si.nPage, si.nMin, si.nMax);
2452 
2453   /* Setting non-scrolling text again does *not* reset scrollbar range */
2454   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2455   memset(&si, 0, sizeof(si));
2456   si.cbSize = sizeof(si);
2457   si.fMask = SIF_PAGE | SIF_RANGE;
2458   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2459   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2460     "Vertical scrollbar is invisible, should be visible.\n");
2461   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2462         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2463         si.nPage, si.nMin, si.nMax);
2464 
2465   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2466   memset(&si, 0, sizeof(si));
2467   si.cbSize = sizeof(si);
2468   si.fMask = SIF_PAGE | SIF_RANGE;
2469   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2470   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2471     "Vertical scrollbar is invisible, should be visible.\n");
2472   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2473         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2474         si.nPage, si.nMin, si.nMax);
2475 
2476   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2477   memset(&si, 0, sizeof(si));
2478   si.cbSize = sizeof(si);
2479   si.fMask = SIF_PAGE | SIF_RANGE;
2480   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2481   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2482     "Vertical scrollbar is invisible, should be visible.\n");
2483   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2484         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2485         si.nPage, si.nMin, si.nMax);
2486 
2487   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2488   memset(&si, 0, sizeof(si));
2489   si.cbSize = sizeof(si);
2490   si.fMask = SIF_PAGE | SIF_RANGE;
2491   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2492   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2493     "Vertical scrollbar is invisible, should be visible.\n");
2494   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2495         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2496         si.nPage, si.nMin, si.nMax);
2497 
2498   DestroyWindow(hwndRichEdit);
2499 
2500   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2501   hwndRichEdit = new_richedit(NULL);
2502 
2503   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2504   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2505   memset(&si, 0, sizeof(si));
2506   si.cbSize = sizeof(si);
2507   si.fMask = SIF_PAGE | SIF_RANGE;
2508   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2510     "Vertical scrollbar is invisible, should be visible.\n");
2511   todo_wine {
2512   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2513         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2514         si.nPage, si.nMin, si.nMax);
2515   }
2516 
2517   /* Ditto, see above */
2518   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2519   memset(&si, 0, sizeof(si));
2520   si.cbSize = sizeof(si);
2521   si.fMask = SIF_PAGE | SIF_RANGE;
2522   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2523   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2524     "Vertical scrollbar is invisible, should be visible.\n");
2525   todo_wine {
2526   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2527         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2528         si.nPage, si.nMin, si.nMax);
2529   }
2530 
2531   /* Ditto, see above */
2532   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2533   memset(&si, 0, sizeof(si));
2534   si.cbSize = sizeof(si);
2535   si.fMask = SIF_PAGE | SIF_RANGE;
2536   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2537   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2538     "Vertical scrollbar is invisible, should be visible.\n");
2539   todo_wine {
2540   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2541         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2542         si.nPage, si.nMin, si.nMax);
2543   }
2544 
2545   /* Ditto, see above */
2546   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2547   memset(&si, 0, sizeof(si));
2548   si.cbSize = sizeof(si);
2549   si.fMask = SIF_PAGE | SIF_RANGE;
2550   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2551   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2552     "Vertical scrollbar is invisible, should be visible.\n");
2553   todo_wine {
2554   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2555         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2556         si.nPage, si.nMin, si.nMax);
2557   }
2558 
2559   /* Ditto, see above */
2560   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2561   memset(&si, 0, sizeof(si));
2562   si.cbSize = sizeof(si);
2563   si.fMask = SIF_PAGE | SIF_RANGE;
2564   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566     "Vertical scrollbar is invisible, should be visible.\n");
2567   todo_wine {
2568   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2569         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2570         si.nPage, si.nMin, si.nMax);
2571   }
2572 
2573   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2574   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2575   memset(&si, 0, sizeof(si));
2576   si.cbSize = sizeof(si);
2577   si.fMask = SIF_PAGE | SIF_RANGE;
2578   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2579   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2580     "Vertical scrollbar is visible, should be invisible.\n");
2581   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2582         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2583         si.nPage, si.nMin, si.nMax);
2584 
2585   DestroyWindow(hwndRichEdit);
2586 
2587   hwndRichEdit = new_richedit(NULL);
2588 
2589   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2590   memset(&si, 0, sizeof(si));
2591   si.cbSize = sizeof(si);
2592   si.fMask = SIF_PAGE | SIF_RANGE;
2593   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2595     "Vertical scrollbar is visible, should be invisible.\n");
2596   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2597         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2598         si.nPage, si.nMin, si.nMax);
2599 
2600   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601   memset(&si, 0, sizeof(si));
2602   si.cbSize = sizeof(si);
2603   si.fMask = SIF_PAGE | SIF_RANGE;
2604   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2606     "Vertical scrollbar is visible, should be invisible.\n");
2607   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2608         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2609         si.nPage, si.nMin, si.nMax);
2610 
2611   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2612   memset(&si, 0, sizeof(si));
2613   si.cbSize = sizeof(si);
2614   si.fMask = SIF_PAGE | SIF_RANGE;
2615   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2617     "Vertical scrollbar is visible, should be invisible.\n");
2618   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2619         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2620         si.nPage, si.nMin, si.nMax);
2621 
2622   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2623   memset(&si, 0, sizeof(si));
2624   si.cbSize = sizeof(si);
2625   si.fMask = SIF_PAGE | SIF_RANGE;
2626   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2627   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2628     "Vertical scrollbar is visible, should be invisible.\n");
2629   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2630         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2631         si.nPage, si.nMin, si.nMax);
2632 
2633   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2634   memset(&si, 0, sizeof(si));
2635   si.cbSize = sizeof(si);
2636   si.fMask = SIF_PAGE | SIF_RANGE;
2637   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2638   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2639     "Vertical scrollbar is invisible, should be visible.\n");
2640   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2641         "reported page/range is %d (%d..%d)\n",
2642         si.nPage, si.nMin, si.nMax);
2643 
2644   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2645   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2646   memset(&si, 0, sizeof(si));
2647   si.cbSize = sizeof(si);
2648   si.fMask = SIF_PAGE | SIF_RANGE;
2649   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2651     "Vertical scrollbar is visible, should be invisible.\n");
2652   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2653         "reported page/range is %d (%d..%d)\n",
2654         si.nPage, si.nMin, si.nMax);
2655 
2656   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2657   memset(&si, 0, sizeof(si));
2658   si.cbSize = sizeof(si);
2659   si.fMask = SIF_PAGE | SIF_RANGE;
2660   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2661   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2662     "Vertical scrollbar is visible, should be invisible.\n");
2663   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2664         "reported page/range is %d (%d..%d)\n",
2665         si.nPage, si.nMin, si.nMax);
2666 
2667   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2668      EM_SCROLL will make visible any forcefully invisible scrollbar */
2669   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2670   memset(&si, 0, sizeof(si));
2671   si.cbSize = sizeof(si);
2672   si.fMask = SIF_PAGE | SIF_RANGE;
2673   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2674   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2675     "Vertical scrollbar is invisible, should be visible.\n");
2676   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2677         "reported page/range is %d (%d..%d)\n",
2678         si.nPage, si.nMin, si.nMax);
2679 
2680   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2681   memset(&si, 0, sizeof(si));
2682   si.cbSize = sizeof(si);
2683   si.fMask = SIF_PAGE | SIF_RANGE;
2684   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2686     "Vertical scrollbar is visible, should be invisible.\n");
2687   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2688         "reported page/range is %d (%d..%d)\n",
2689         si.nPage, si.nMin, si.nMax);
2690 
2691   /* Again, EM_SCROLL, with SB_LINEUP */
2692   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2693   memset(&si, 0, sizeof(si));
2694   si.cbSize = sizeof(si);
2695   si.fMask = SIF_PAGE | SIF_RANGE;
2696   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2697   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2698     "Vertical scrollbar is invisible, should be visible.\n");
2699   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2700         "reported page/range is %d (%d..%d)\n",
2701         si.nPage, si.nMin, si.nMax);
2702 
2703   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2704   memset(&si, 0, sizeof(si));
2705   si.cbSize = sizeof(si);
2706   si.fMask = SIF_PAGE | SIF_RANGE;
2707   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2708   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2709     "Vertical scrollbar is visible, should be invisible.\n");
2710   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2711         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2712         si.nPage, si.nMin, si.nMax);
2713 
2714   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2715   memset(&si, 0, sizeof(si));
2716   si.cbSize = sizeof(si);
2717   si.fMask = SIF_PAGE | SIF_RANGE;
2718   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2719   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2720     "Vertical scrollbar is invisible, should be visible.\n");
2721   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2722         "reported page/range is %d (%d..%d)\n",
2723         si.nPage, si.nMin, si.nMax);
2724 
2725   DestroyWindow(hwndRichEdit);
2726 
2727 
2728   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2729   hwndRichEdit = new_richedit(NULL);
2730 
2731 #define ENABLE_WS_VSCROLL(hwnd) \
2732     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2733 #define DISABLE_WS_VSCROLL(hwnd) \
2734     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2735 
2736   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2737   ENABLE_WS_VSCROLL(hwndRichEdit);
2738   memset(&si, 0, sizeof(si));
2739   si.cbSize = sizeof(si);
2740   si.fMask = SIF_PAGE | SIF_RANGE;
2741   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2742   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2743     "Vertical scrollbar is invisible, should be visible.\n");
2744   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2745         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2746         si.nPage, si.nMin, si.nMax);
2747 
2748   /* Ditto, see above */
2749   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2750   memset(&si, 0, sizeof(si));
2751   si.cbSize = sizeof(si);
2752   si.fMask = SIF_PAGE | SIF_RANGE;
2753   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2754   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2755     "Vertical scrollbar is invisible, should be visible.\n");
2756   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2757         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2758         si.nPage, si.nMin, si.nMax);
2759 
2760   /* Ditto, see above */
2761   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2762   memset(&si, 0, sizeof(si));
2763   si.cbSize = sizeof(si);
2764   si.fMask = SIF_PAGE | SIF_RANGE;
2765   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2766   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2767     "Vertical scrollbar is invisible, should be visible.\n");
2768   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2769         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2770         si.nPage, si.nMin, si.nMax);
2771 
2772   /* Ditto, see above */
2773   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2774   memset(&si, 0, sizeof(si));
2775   si.cbSize = sizeof(si);
2776   si.fMask = SIF_PAGE | SIF_RANGE;
2777   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2778   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2779     "Vertical scrollbar is invisible, should be visible.\n");
2780   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2781         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2782         si.nPage, si.nMin, si.nMax);
2783 
2784   /* Ditto, see above */
2785   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2786   memset(&si, 0, sizeof(si));
2787   si.cbSize = sizeof(si);
2788   si.fMask = SIF_PAGE | SIF_RANGE;
2789   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2790   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2791     "Vertical scrollbar is invisible, should be visible.\n");
2792   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2793         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2794         si.nPage, si.nMin, si.nMax);
2795 
2796   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2797   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2798   memset(&si, 0, sizeof(si));
2799   si.cbSize = sizeof(si);
2800   si.fMask = SIF_PAGE | SIF_RANGE;
2801   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2803     "Vertical scrollbar is visible, should be invisible.\n");
2804   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2805         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2806         si.nPage, si.nMin, si.nMax);
2807 
2808   DestroyWindow(hwndRichEdit);
2809 
2810   hwndRichEdit = new_richedit(NULL);
2811 
2812   DISABLE_WS_VSCROLL(hwndRichEdit);
2813   memset(&si, 0, sizeof(si));
2814   si.cbSize = sizeof(si);
2815   si.fMask = SIF_PAGE | SIF_RANGE;
2816   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2817   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2818     "Vertical scrollbar is visible, should be invisible.\n");
2819   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2820         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2821         si.nPage, si.nMin, si.nMax);
2822 
2823   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2824   memset(&si, 0, sizeof(si));
2825   si.cbSize = sizeof(si);
2826   si.fMask = SIF_PAGE | SIF_RANGE;
2827   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2828   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2829     "Vertical scrollbar is visible, should be invisible.\n");
2830   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2831         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2832         si.nPage, si.nMin, si.nMax);
2833 
2834   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2835   memset(&si, 0, sizeof(si));
2836   si.cbSize = sizeof(si);
2837   si.fMask = SIF_PAGE | SIF_RANGE;
2838   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2839   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2840     "Vertical scrollbar is visible, should be invisible.\n");
2841   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2842         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2843         si.nPage, si.nMin, si.nMax);
2844 
2845   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2846   memset(&si, 0, sizeof(si));
2847   si.cbSize = sizeof(si);
2848   si.fMask = SIF_PAGE | SIF_RANGE;
2849   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2850   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2851     "Vertical scrollbar is visible, should be invisible.\n");
2852   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2853         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2854         si.nPage, si.nMin, si.nMax);
2855 
2856   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2857   memset(&si, 0, sizeof(si));
2858   si.cbSize = sizeof(si);
2859   si.fMask = SIF_PAGE | SIF_RANGE;
2860   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2861   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2862     "Vertical scrollbar is invisible, should be visible.\n");
2863   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2864         "reported page/range is %d (%d..%d)\n",
2865         si.nPage, si.nMin, si.nMax);
2866 
2867   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2868   DISABLE_WS_VSCROLL(hwndRichEdit);
2869   memset(&si, 0, sizeof(si));
2870   si.cbSize = sizeof(si);
2871   si.fMask = SIF_PAGE | SIF_RANGE;
2872   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2873   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2874     "Vertical scrollbar is visible, should be invisible.\n");
2875   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2876         "reported page/range is %d (%d..%d)\n",
2877         si.nPage, si.nMin, si.nMax);
2878 
2879   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2880   memset(&si, 0, sizeof(si));
2881   si.cbSize = sizeof(si);
2882   si.fMask = SIF_PAGE | SIF_RANGE;
2883   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2885     "Vertical scrollbar is visible, should be invisible.\n");
2886   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2887         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2888         si.nPage, si.nMin, si.nMax);
2889 
2890   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2891   memset(&si, 0, sizeof(si));
2892   si.cbSize = sizeof(si);
2893   si.fMask = SIF_PAGE | SIF_RANGE;
2894   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2895   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2896     "Vertical scrollbar is invisible, should be visible.\n");
2897   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2898         "reported page/range is %d (%d..%d)\n",
2899         si.nPage, si.nMin, si.nMax);
2900 
2901   DISABLE_WS_VSCROLL(hwndRichEdit);
2902   memset(&si, 0, sizeof(si));
2903   si.cbSize = sizeof(si);
2904   si.fMask = SIF_PAGE | SIF_RANGE;
2905   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2906   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2907     "Vertical scrollbar is visible, should be invisible.\n");
2908   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2909         "reported page/range is %d (%d..%d)\n",
2910         si.nPage, si.nMin, si.nMax);
2911 
2912   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2913      EM_SCROLL will make visible any forcefully invisible scrollbar */
2914   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2915   memset(&si, 0, sizeof(si));
2916   si.cbSize = sizeof(si);
2917   si.fMask = SIF_PAGE | SIF_RANGE;
2918   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2920     "Vertical scrollbar is invisible, should be visible.\n");
2921   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2922         "reported page/range is %d (%d..%d)\n",
2923         si.nPage, si.nMin, si.nMax);
2924 
2925   DISABLE_WS_VSCROLL(hwndRichEdit);
2926   memset(&si, 0, sizeof(si));
2927   si.cbSize = sizeof(si);
2928   si.fMask = SIF_PAGE | SIF_RANGE;
2929   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931     "Vertical scrollbar is visible, should be invisible.\n");
2932   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2933         "reported page/range is %d (%d..%d)\n",
2934         si.nPage, si.nMin, si.nMax);
2935 
2936   /* Again, EM_SCROLL, with SB_LINEUP */
2937   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2938   memset(&si, 0, sizeof(si));
2939   si.cbSize = sizeof(si);
2940   si.fMask = SIF_PAGE | SIF_RANGE;
2941   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2942   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2943     "Vertical scrollbar is invisible, should be visible.\n");
2944   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2945         "reported page/range is %d (%d..%d)\n",
2946         si.nPage, si.nMin, si.nMax);
2947 
2948   DestroyWindow(hwndRichEdit);
2949 
2950   /* This window proc models what is going on with Corman Lisp 3.0.
2951      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2952      force the scrollbar into visibility. Recursion should NOT happen
2953      as a result of this action.
2954    */
2955   r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2956   if (r) {
2957     richeditProc = cls.lpfnWndProc;
2958     cls.lpfnWndProc = RicheditStupidOverrideProcA;
2959     cls.lpszClassName = "RicheditStupidOverride";
2960     if(!RegisterClassA(&cls)) assert(0);
2961 
2962     recursionLevel = 0;
2963     WM_SIZE_recursionLevel = 0;
2964     bailedOutOfRecursion = FALSE;
2965     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2966     ok(!bailedOutOfRecursion,
2967         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2968 
2969     recursionLevel = 0;
2970     WM_SIZE_recursionLevel = 0;
2971     bailedOutOfRecursion = FALSE;
2972     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2973     ok(!bailedOutOfRecursion,
2974         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2975 
2976     /* Unblock window in order to process WM_DESTROY */
2977     recursionLevel = 0;