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