~ [ 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   DestroyWindow(hwndRichEdit);
653 }
654 
655 static void test_EM_SETCHARFORMAT(void)
656 {
657   HWND hwndRichEdit = new_richedit(NULL);
658   CHARFORMAT2 cf2;
659   int rc = 0;
660   int tested_effects[] = {
661     CFE_BOLD,
662     CFE_ITALIC,
663     CFE_UNDERLINE,
664     CFE_STRIKEOUT,
665     CFE_PROTECTED,
666     CFE_LINK,
667     CFE_SUBSCRIPT,
668     CFE_SUPERSCRIPT,
669     0
670   };
671   int i;
672   CHARRANGE cr;
673 
674   /* Invalid flags, CHARFORMAT2 structure blanked out */
675   memset(&cf2, 0, sizeof(cf2));
676   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
677              (LPARAM) &cf2);
678   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
679 
680   /* A valid flag, CHARFORMAT2 structure blanked out */
681   memset(&cf2, 0, sizeof(cf2));
682   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
683              (LPARAM) &cf2);
684   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
685 
686   /* A valid flag, CHARFORMAT2 structure blanked out */
687   memset(&cf2, 0, sizeof(cf2));
688   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
689              (LPARAM) &cf2);
690   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
691 
692   /* A valid flag, CHARFORMAT2 structure blanked out */
693   memset(&cf2, 0, sizeof(cf2));
694   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
695              (LPARAM) &cf2);
696   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
697 
698   /* A valid flag, CHARFORMAT2 structure blanked out */
699   memset(&cf2, 0, sizeof(cf2));
700   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
701              (LPARAM) &cf2);
702   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
703 
704   /* Invalid flags, CHARFORMAT2 structure minimally filled */
705   memset(&cf2, 0, sizeof(cf2));
706   cf2.cbSize = sizeof(CHARFORMAT2);
707   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
708              (LPARAM) &cf2);
709   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
710   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
711   ok(rc == FALSE, "Should not be able to undo here.\n");
712   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
713 
714   /* A valid flag, CHARFORMAT2 structure minimally filled */
715   memset(&cf2, 0, sizeof(cf2));
716   cf2.cbSize = sizeof(CHARFORMAT2);
717   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
718              (LPARAM) &cf2);
719   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
720   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
721   ok(rc == FALSE, "Should not be able to undo here.\n");
722   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
723 
724   /* A valid flag, CHARFORMAT2 structure minimally filled */
725   memset(&cf2, 0, sizeof(cf2));
726   cf2.cbSize = sizeof(CHARFORMAT2);
727   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
728              (LPARAM) &cf2);
729   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
731   ok(rc == FALSE, "Should not be able to undo here.\n");
732   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
733 
734   /* A valid flag, CHARFORMAT2 structure minimally filled */
735   memset(&cf2, 0, sizeof(cf2));
736   cf2.cbSize = sizeof(CHARFORMAT2);
737   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
738              (LPARAM) &cf2);
739   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
740   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
741   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
742   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
743 
744   /* A valid flag, CHARFORMAT2 structure minimally filled */
745   memset(&cf2, 0, sizeof(cf2));
746   cf2.cbSize = sizeof(CHARFORMAT2);
747   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
748              (LPARAM) &cf2);
749   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
750   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
751   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
752   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
753 
754   cf2.cbSize = sizeof(CHARFORMAT2);
755   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
756              (LPARAM) &cf2);
757 
758   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
759   cf2.cbSize = sizeof(CHARFORMAT2);
760   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
761              (LPARAM) &cf2);
762   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
763   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
764 
765   /* wParam==0 is default char format, does not set modify */
766   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
767   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
768   ok(rc == 0, "Text marked as modified, expected not modified!\n");
769   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
770   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772   ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 
774   /* wParam==SCF_SELECTION sets modify if nonempty selection */
775   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
776   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
777   ok(rc == 0, "Text marked as modified, expected not modified!\n");
778   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
779   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
780   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
781   ok(rc == 0, "Text marked as modified, expected not modified!\n");
782 
783   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
784   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
785   ok(rc == 0, "Text marked as modified, expected not modified!\n");
786   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789   ok(rc == 0, "Text marked as modified, expected not modified!\n");
790   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
791   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
792   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
793   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
795 
796   /* wParam==SCF_ALL sets modify regardless of whether text is present */
797   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
798   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
799   ok(rc == 0, "Text marked as modified, expected not modified!\n");
800   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
801   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
802   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
804 
805   DestroyWindow(hwndRichEdit);
806 
807   /* EM_GETCHARFORMAT tests */
808   for (i = 0; tested_effects[i]; i++)
809   {
810     hwndRichEdit = new_richedit(NULL);
811     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
812 
813     /* Need to set a TrueType font to get consistent CFM_BOLD results */
814     memset(&cf2, 0, sizeof(CHARFORMAT2));
815     cf2.cbSize = sizeof(CHARFORMAT2);
816     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
817     cf2.dwEffects = 0;
818     strcpy(cf2.szFaceName, "Courier New");
819     cf2.wWeight = FW_DONTCARE;
820     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
821 
822     memset(&cf2, 0, sizeof(CHARFORMAT2));
823     cf2.cbSize = sizeof(CHARFORMAT2);
824     SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
825     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
826     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
827           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
828           ||
829           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
830         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
831     ok((cf2.dwEffects & tested_effects[i]) == 0,
832         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
833 
834     memset(&cf2, 0, sizeof(CHARFORMAT2));
835     cf2.cbSize = sizeof(CHARFORMAT2);
836     cf2.dwMask = tested_effects[i];
837     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
838       cf2.dwMask = CFM_SUPERSCRIPT;
839     cf2.dwEffects = tested_effects[i];
840     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 
843     memset(&cf2, 0, sizeof(CHARFORMAT2));
844     cf2.cbSize = sizeof(CHARFORMAT2);
845     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
846     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
847     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
848           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
849           ||
850           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
851         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
852     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
853         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
854 
855     memset(&cf2, 0, sizeof(CHARFORMAT2));
856     cf2.cbSize = sizeof(CHARFORMAT2);
857     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
858     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
859     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
860           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
861           ||
862           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
863         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
864     ok((cf2.dwEffects & tested_effects[i]) == 0,
865         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
866 
867     memset(&cf2, 0, sizeof(CHARFORMAT2));
868     cf2.cbSize = sizeof(CHARFORMAT2);
869     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
870     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
871     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
872           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
873           ||
874           (cf2.dwMask & tested_effects[i]) == 0),
875         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
876 
877     DestroyWindow(hwndRichEdit);
878   }
879 
880   for (i = 0; tested_effects[i]; i++)
881   {
882     hwndRichEdit = new_richedit(NULL);
883     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
884 
885     /* Need to set a TrueType font to get consistent CFM_BOLD results */
886     memset(&cf2, 0, sizeof(CHARFORMAT2));
887     cf2.cbSize = sizeof(CHARFORMAT2);
888     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
889     cf2.dwEffects = 0;
890     strcpy(cf2.szFaceName, "Courier New");
891     cf2.wWeight = FW_DONTCARE;
892     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
893 
894     memset(&cf2, 0, sizeof(CHARFORMAT2));
895     cf2.cbSize = sizeof(CHARFORMAT2);
896     cf2.dwMask = tested_effects[i];
897     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
898       cf2.dwMask = CFM_SUPERSCRIPT;
899     cf2.dwEffects = tested_effects[i];
900     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
901     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
902 
903     memset(&cf2, 0, sizeof(CHARFORMAT2));
904     cf2.cbSize = sizeof(CHARFORMAT2);
905     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
906     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
907     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
908           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
909           ||
910           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
911         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
912     ok((cf2.dwEffects & tested_effects[i]) == 0,
913         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
914 
915     memset(&cf2, 0, sizeof(CHARFORMAT2));
916     cf2.cbSize = sizeof(CHARFORMAT2);
917     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
918     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
919     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
920           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
921           ||
922           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
923         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
924     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
925         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
926 
927     memset(&cf2, 0, sizeof(CHARFORMAT2));
928     cf2.cbSize = sizeof(CHARFORMAT2);
929     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
930     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
931     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
932           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
933           ||
934           (cf2.dwMask & tested_effects[i]) == 0),
935         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
936     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
937         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
938 
939     DestroyWindow(hwndRichEdit);
940   }
941 
942   /* Effects applied on an empty selection should take effect when selection is
943      replaced with text */
944   hwndRichEdit = new_richedit(NULL);
945   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
946   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
947 
948   memset(&cf2, 0, sizeof(CHARFORMAT2));
949   cf2.cbSize = sizeof(CHARFORMAT2);
950   cf2.dwMask = CFM_BOLD;
951   cf2.dwEffects = CFE_BOLD;
952   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
953 
954   /* Selection is now nonempty */
955   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
956 
957   memset(&cf2, 0, sizeof(CHARFORMAT2));
958   cf2.cbSize = sizeof(CHARFORMAT2);
959   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
960   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
961 
962   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
963       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
964   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
965       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
966 
967 
968   /* Set two effects on an empty selection */
969   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
970   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
971 
972   memset(&cf2, 0, sizeof(CHARFORMAT2));
973   cf2.cbSize = sizeof(CHARFORMAT2);
974   cf2.dwMask = CFM_BOLD;
975   cf2.dwEffects = CFE_BOLD;
976   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
977   cf2.dwMask = CFM_ITALIC;
978   cf2.dwEffects = CFE_ITALIC;
979   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
980 
981   /* Selection is now nonempty */
982   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
983 
984   memset(&cf2, 0, sizeof(CHARFORMAT2));
985   cf2.cbSize = sizeof(CHARFORMAT2);
986   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
987   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
988 
989   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
990       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
991   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
992       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
993 
994   /* Setting the (empty) selection to exactly the same place as before should
995      NOT clear the insertion style! */
996   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
997   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
998 
999   memset(&cf2, 0, sizeof(CHARFORMAT2));
1000   cf2.cbSize = sizeof(CHARFORMAT2);
1001   cf2.dwMask = CFM_BOLD;
1002   cf2.dwEffects = CFE_BOLD;
1003   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1004 
1005   /* Empty selection in same place, insert style should NOT be forgotten here. */
1006   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1007 
1008   /* Selection is now nonempty */
1009   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1010 
1011   memset(&cf2, 0, sizeof(CHARFORMAT2));
1012   cf2.cbSize = sizeof(CHARFORMAT2);
1013   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1014   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1015 
1016   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1017       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1018   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1019       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1020 
1021   /* Ditto with EM_EXSETSEL */
1022   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023   cr.cpMin = 2; cr.cpMax = 2;
1024   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1025 
1026   memset(&cf2, 0, sizeof(CHARFORMAT2));
1027   cf2.cbSize = sizeof(CHARFORMAT2);
1028   cf2.dwMask = CFM_BOLD;
1029   cf2.dwEffects = CFE_BOLD;
1030   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1031 
1032   /* Empty selection in same place, insert style should NOT be forgotten here. */
1033   cr.cpMin = 2; cr.cpMax = 2;
1034   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1035 
1036   /* Selection is now nonempty */
1037   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1038 
1039   memset(&cf2, 0, sizeof(CHARFORMAT2));
1040   cf2.cbSize = sizeof(CHARFORMAT2);
1041   cr.cpMin = 2; cr.cpMax = 6;
1042   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1043   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1044 
1045   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1046       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1047   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1048       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1049 
1050   DestroyWindow(hwndRichEdit);
1051 }
1052 
1053 static void test_EM_SETTEXTMODE(void)
1054 {
1055   HWND hwndRichEdit = new_richedit(NULL);
1056   CHARFORMAT2 cf2, cf2test;
1057   CHARRANGE cr;
1058   int rc = 0;
1059 
1060   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1061   /*Insert text into the control*/
1062 
1063   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1064 
1065   /*Attempt to change the control to plain text mode*/
1066   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1067   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1068 
1069   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1070   If rich text is pasted, it should have the same formatting as the rest
1071   of the text in the control*/
1072 
1073   /*Italicize the text
1074   *NOTE: If the default text was already italicized, the test will simply
1075   reverse; in other words, it will copy a regular "wine" into a plain
1076   text window that uses an italicized format*/
1077   cf2.cbSize = sizeof(CHARFORMAT2);
1078   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1079              (LPARAM) &cf2);
1080 
1081   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1082   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1083 
1084   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1085   ok(rc == 0, "Text marked as modified, expected not modified!\n");
1086 
1087   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1088   however, SCF_ALL has been implemented*/
1089   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1090   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1091 
1092   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1093   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1094 
1095   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1096 
1097   /*Select the string "wine"*/
1098   cr.cpMin = 0;
1099   cr.cpMax = 4;
1100   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1101 
1102   /*Copy the italicized "wine" to the clipboard*/
1103   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1104 
1105   /*Reset the formatting to default*/
1106   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1107   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1108   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1109 
1110   /*Clear the text in the control*/
1111   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1112 
1113   /*Switch to Plain Text Mode*/
1114   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1115   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1116 
1117   /*Input "wine" again in normal format*/
1118   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1119 
1120   /*Paste the italicized "wine" into the control*/
1121   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1122 
1123   /*Select a character from the first "wine" string*/
1124   cr.cpMin = 2;
1125   cr.cpMax = 3;
1126   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1127 
1128   /*Retrieve its formatting*/
1129   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1130               (LPARAM) &cf2);
1131 
1132   /*Select a character from the second "wine" string*/
1133   cr.cpMin = 5;
1134   cr.cpMax = 6;
1135   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1136 
1137   /*Retrieve its formatting*/
1138   cf2test.cbSize = sizeof(CHARFORMAT2);
1139   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1140                (LPARAM) &cf2test);
1141 
1142   /*Compare the two formattings*/
1143     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1144       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1145        cf2.dwEffects, cf2test.dwEffects);
1146   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1147                          printing "wine" in the current format(normal)
1148                          pasting "wine" from the clipboard(italicized)
1149                          comparing the two formats(should differ)*/
1150 
1151   /*Attempt to switch with text in control*/
1152   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1153   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1154 
1155   /*Clear control*/
1156   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1157 
1158   /*Switch into Rich Text mode*/
1159   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1160   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1161 
1162   /*Print "wine" in normal formatting into the control*/
1163   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1164 
1165   /*Paste italicized "wine" into the control*/
1166   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1167 
1168   /*Select text from the first "wine" string*/
1169   cr.cpMin = 1;
1170   cr.cpMax = 3;
1171   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1172 
1173   /*Retrieve its formatting*/
1174   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1175                 (LPARAM) &cf2);
1176 
1177   /*Select text from the second "wine" string*/
1178   cr.cpMin = 6;
1179   cr.cpMax = 7;
1180   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1181 
1182   /*Retrieve its formatting*/
1183   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1184                 (LPARAM) &cf2test);
1185 
1186   /*Test that the two formattings are not the same*/
1187   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1188       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1189       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1190 
1191   DestroyWindow(hwndRichEdit);
1192 }
1193 
1194 static void test_SETPARAFORMAT(void)
1195 {
1196   HWND hwndRichEdit = new_richedit(NULL);
1197   PARAFORMAT2 fmt;
1198   HRESULT ret;
1199   LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1200   fmt.cbSize = sizeof(PARAFORMAT2);
1201   fmt.dwMask = PFM_ALIGNMENT;
1202   fmt.wAlignment = PFA_LEFT;
1203 
1204   ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1205   ok(ret != 0, "expected non-zero got %d\n", ret);
1206 
1207   fmt.cbSize = sizeof(PARAFORMAT2);
1208   fmt.dwMask = -1;
1209   ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1210   /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1211    * between richedit different native builds of riched20.dll
1212    * used on different Windows versions. */
1213   ret &= ~PFM_TABLEROWDELIMITER;
1214   fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1215 
1216   ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1217   ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1218 
1219   DestroyWindow(hwndRichEdit);
1220 }
1221 
1222 static void test_TM_PLAINTEXT(void)
1223 {
1224   /*Tests plain text properties*/
1225 
1226   HWND hwndRichEdit = new_richedit(NULL);
1227   CHARFORMAT2 cf2, cf2test;
1228   CHARRANGE cr;
1229   int rc = 0;
1230 
1231   /*Switch to plain text mode*/
1232 
1233   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1234   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1235 
1236   /*Fill control with text*/
1237 
1238   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1239 
1240   /*Select some text and bold it*/
1241 
1242   cr.cpMin = 10;
1243   cr.cpMax = 20;
1244   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1245   cf2.cbSize = sizeof(CHARFORMAT2);
1246   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1247               (LPARAM) &cf2);
1248 
1249   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1250   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1251 
1252   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1253   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1254 
1255   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1256   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1257 
1258   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1259   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1260 
1261   /*Get the formatting of those characters*/
1262 
1263   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1264 
1265   /*Get the formatting of some other characters*/
1266   cf2test.cbSize = sizeof(CHARFORMAT2);
1267   cr.cpMin = 21;
1268   cr.cpMax = 30;
1269   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1270   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1271 
1272   /*Test that they are the same as plain text allows only one formatting*/
1273 
1274   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1275      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1276      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1277   
1278   /*Fill the control with a "wine" string, which when inserted will be bold*/
1279 
1280   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1281 
1282   /*Copy the bolded "wine" string*/
1283 
1284   cr.cpMin = 0;
1285   cr.cpMax = 4;
1286   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1287   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1288 
1289   /*Swap back to rich text*/
1290 
1291   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1292   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1293 
1294   /*Set the default formatting to bold italics*/
1295 
1296   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1297   cf2.dwMask |= CFM_ITALIC;
1298   cf2.dwEffects ^= CFE_ITALIC;
1299   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1300   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1301 
1302   /*Set the text in the control to "wine", which will be bold and italicized*/
1303 
1304   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1305 
1306   /*Paste the plain text "wine" string, which should take the insert
1307    formatting, which at the moment is bold italics*/
1308 
1309   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1310 
1311   /*Select the first "wine" string and retrieve its formatting*/
1312 
1313   cr.cpMin = 1;
1314   cr.cpMax = 3;
1315   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1316   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1317 
1318   /*Select the second "wine" string and retrieve its formatting*/
1319 
1320   cr.cpMin = 5;
1321   cr.cpMax = 7;
1322   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1323   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1324 
1325   /*Compare the two formattings. They should be the same.*/
1326 
1327   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1328      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1329      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1330   DestroyWindow(hwndRichEdit);
1331 }
1332 
1333 static void test_WM_GETTEXT(void)
1334 {
1335     HWND hwndRichEdit = new_richedit(NULL);
1336     static const char text[] = "Hello. My name is RichEdit!";
1337     static const char text2[] = "Hello. My name is RichEdit!\r";
1338     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1339     char buffer[1024] = {0};
1340     int result;
1341 
1342     /* Baseline test with normal-sized buffer */
1343     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1344     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1345     ok(result == lstrlen(buffer),
1346         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1347     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1348     result = strcmp(buffer,text);
1349     ok(result == 0, 
1350         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1351 
1352     /* Test for returned value of WM_GETTEXTLENGTH */
1353     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1354     ok(result == lstrlen(text),
1355         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1356         result, lstrlen(text));
1357 
1358     /* Test for behavior in overflow case */
1359     memset(buffer, 0, 1024);
1360     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1361     ok(result == 0 ||
1362        result == lstrlenA(text) - 1, /* XP, win2k3 */
1363         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1364     result = strcmp(buffer,text);
1365     if (result)
1366         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1367     ok(result == 0,
1368         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1369 
1370     /* Baseline test with normal-sized buffer and carriage return */
1371     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1372     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1373     ok(result == lstrlen(buffer),
1374         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1375     result = strcmp(buffer,text2_after);
1376     ok(result == 0,
1377         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1378 
1379     /* Test for returned value of WM_GETTEXTLENGTH */
1380     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1381     ok(result == lstrlen(text2_after),
1382         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1383         result, lstrlen(text2_after));
1384 
1385     /* Test for behavior of CRLF conversion in case of overflow */
1386     memset(buffer, 0, 1024);
1387     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1388     ok(result == 0 ||
1389        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1390         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1391     result = strcmp(buffer,text2);
1392     if (result)
1393         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1394     ok(result == 0,
1395         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1396 
1397     DestroyWindow(hwndRichEdit);
1398 }
1399 
1400 static void test_EM_GETTEXTRANGE(void)
1401 {
1402     HWND hwndRichEdit = new_richedit(NULL);
1403     const char * text1 = "foo bar\r\nfoo bar";
1404     const char * text2 = "foo bar\rfoo bar";
1405     const char * expect = "bar\rfoo";
1406     char buffer[1024] = {0};
1407     LRESULT result;
1408     TEXTRANGEA textRange;
1409 
1410     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1411 
1412     textRange.lpstrText = buffer;
1413     textRange.chrg.cpMin = 4;
1414     textRange.chrg.cpMax = 11;
1415     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1416     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1417     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1418 
1419     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1420 
1421     textRange.lpstrText = buffer;
1422     textRange.chrg.cpMin = 4;
1423     textRange.chrg.cpMax = 11;
1424     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1425     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1426     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1427 
1428     DestroyWindow(hwndRichEdit);
1429 }
1430 
1431 static void test_EM_GETSELTEXT(void)
1432 {
1433     HWND hwndRichEdit = new_richedit(NULL);
1434     const char * text1 = "foo bar\r\nfoo bar";
1435     const char * text2 = "foo bar\rfoo bar";
1436     const char * expect = "bar\rfoo";
1437     char buffer[1024] = {0};
1438     LRESULT result;
1439 
1440     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1441 
1442     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1443     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1444     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1445     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446 
1447     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1448 
1449     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1450     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1451     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1452     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1453 
1454     DestroyWindow(hwndRichEdit);
1455 }
1456 
1457 /* FIXME: need to test unimplemented options and robustly test wparam */
1458 static void test_EM_SETOPTIONS(void)
1459 {
1460     HWND hwndRichEdit = new_richedit(NULL);
1461     static const char text[] = "Hello. My name is RichEdit!";
1462     char buffer[1024] = {0};
1463 
1464     /* NEGATIVE TESTING - NO OPTIONS SET */
1465     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1466     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1467 
1468     /* testing no readonly by sending 'a' to the control*/
1469     SetFocus(hwndRichEdit);
1470     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1471     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1472     ok(buffer[0]=='a', 
1473        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1474     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1475 
1476     /* READONLY - sending 'a' to the control */
1477     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1478     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1479     SetFocus(hwndRichEdit);
1480     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1481     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1482     ok(buffer[0]==text[0], 
1483        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1484 
1485     DestroyWindow(hwndRichEdit);
1486 }
1487 
1488 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1489 {
1490   CHARFORMAT2W text_format;
1491   text_format.cbSize = sizeof(text_format);
1492   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1493   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1494   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1495 }
1496 
1497 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1498 {
1499   int link_present = 0;
1500 
1501   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1502   if (is_url) 
1503   { /* control text is url; should get CFE_LINK */
1504         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1505   }
1506   else 
1507   {
1508     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1509   }
1510 }
1511 
1512 static HWND new_static_wnd(HWND parent) {
1513   return new_window("Static", 0, parent);
1514 }
1515 
1516 static void test_EM_AUTOURLDETECT(void)
1517 {
1518   /* DO NOT change the properties of the first two elements. To shorten the
1519      tests, all tests after WM_SETTEXT test just the first two elements -
1520      one non-URL and one URL */
1521   struct urls_s {
1522     const char *text;
1523     int is_url;
1524   } urls[12] = {
1525     {"winehq.org", 0},
1526     {"http://www.winehq.org", 1},
1527     {"http//winehq.org", 0},
1528     {"ww.winehq.org", 0},
1529     {"www.winehq.org", 1},
1530     {"ftp://192.168.1.1", 1},
1531     {"ftp//192.168.1.1", 0},
1532     {"mailto:your@email.com", 1},    
1533     {"prospero:prosperoserver", 1},
1534     {"telnet:test", 1},
1535     {"news:newserver", 1},
1536     {"wais:waisserver", 1}  
1537   };
1538 
1539   int i, j;
1540   int urlRet=-1;
1541   HWND hwndRichEdit, parent;
1542 
1543   /* All of the following should cause the URL to be detected  */
1544   const char * templates_delim[] = {
1545     "This is some text with X on it",
1546     "This is some text with (X) on it",
1547     "This is some text with X\r on it",
1548     "This is some text with ---X--- on it",
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' on it",
1552     "This is some text with :X: on it",
1553 
1554     "This text ends with X",
1555 
1556     "This is some text with X) on it",
1557     "This is some text with X--- on it",
1558     "This is some text with X\" on it",
1559     "This is some text with X' on it",
1560     "This is some text with X: on it",
1561 
1562     "This is some text with (X on it",
1563     "This is some text with \rX on it",
1564     "This is some text with ---X on it",
1565     "This is some text with \"X on it",
1566     "This is some text with 'X on it",
1567     "This is some text with :X on it",
1568   };
1569   /* None of these should cause the URL to be detected */
1570   const char * templates_non_delim[] = {
1571     "This is some text with |X| on it",
1572     "This is some text with *X* on it",
1573     "This is some text with /X/ on it",
1574     "This is some text with +X+ on it",
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   };
1588   /* All of these cause the URL detection to be extended by one more byte,
1589      thus demonstrating that the tested character is considered as part
1590      of the URL. */
1591   const char * templates_xten_delim[] = {
1592     "This is some text with X| on it",
1593     "This is some text with X* on it",
1594     "This is some text with X/ on it",
1595     "This is some text with X+ on it",
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   };
1601   char buffer[1024];
1602 
1603   parent = new_static_wnd(NULL);
1604   hwndRichEdit = new_richedit(parent);
1605   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1606   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1607   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1608   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1609   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1610   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1611   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1612   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1613   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1614   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1615   /* for each url, check the text to see if CFE_LINK effect is present */
1616   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1617 
1618     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1619     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1620     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1621 
1622     /* Link detection should happen immediately upon WM_SETTEXT */
1623     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1624     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1625     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1626   }
1627   DestroyWindow(hwndRichEdit);
1628 
1629   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1630   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1631     hwndRichEdit = new_richedit(parent);
1632 
1633     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1634       char * at_pos;
1635       int at_offset;
1636       int end_offset;
1637 
1638       at_pos = strchr(templates_delim[j], 'X');
1639       at_offset = at_pos - templates_delim[j];
1640       strncpy(buffer, templates_delim[j], at_offset);
1641       buffer[at_offset] = '\0';
1642       strcat(buffer, urls[i].text);
1643       strcat(buffer, templates_delim[j] + at_offset + 1);
1644       end_offset = at_offset + strlen(urls[i].text);
1645 
1646       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1647       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1648 
1649       /* This assumes no templates start with the URL itself, and that they
1650          have at least two characters before the URL text */
1651       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1652         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1653       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1654         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1655       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1656         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1657 
1658       if (urls[i].is_url)
1659       {
1660         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1661           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1662         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1663           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1664       }
1665       else
1666       {
1667         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1668           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1669         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1670           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1671       }
1672       if (buffer[end_offset] != '\0')
1673       {
1674         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1675           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1676         if (buffer[end_offset +1] != '\0')
1677         {
1678           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1679             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1680         }
1681       }
1682     }
1683 
1684     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1685       char * at_pos;
1686       int at_offset;
1687       int end_offset;
1688 
1689       at_pos = strchr(templates_non_delim[j], 'X');
1690       at_offset = at_pos - templates_non_delim[j];
1691       strncpy(buffer, templates_non_delim[j], at_offset);
1692       buffer[at_offset] = '\0';
1693       strcat(buffer, urls[i].text);
1694       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1695       end_offset = at_offset + strlen(urls[i].text);
1696 
1697       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1698       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1699 
1700       /* This assumes no templates start with the URL itself, and that they
1701          have at least two characters before the URL text */
1702       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1703         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1704       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1705         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1706       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1707         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1708 
1709       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1710         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1711       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1712         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1713       if (buffer[end_offset] != '\0')
1714       {
1715         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1716           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1717         if (buffer[end_offset +1] != '\0')
1718         {
1719           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1720             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1721         }
1722       }
1723     }
1724 
1725     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1726       char * at_pos;
1727       int at_offset;
1728       int end_offset;
1729 
1730       at_pos = strchr(templates_xten_delim[j], 'X');
1731       at_offset = at_pos - templates_xten_delim[j];
1732       strncpy(buffer, templates_xten_delim[j], at_offset);
1733       buffer[at_offset] = '\0';
1734       strcat(buffer, urls[i].text);
1735       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1736       end_offset = at_offset + strlen(urls[i].text);
1737 
1738       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1740 
1741       /* This assumes no templates start with the URL itself, and that they
1742          have at least two characters before the URL text */
1743       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1744         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1745       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1746         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1747       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1748         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1749 
1750       if (urls[i].is_url)
1751       {
1752         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1754         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1756         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1757           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1758       }
1759       else
1760       {
1761         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1762           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1763         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1764           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1765         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1766           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1767       }
1768       if (buffer[end_offset +1] != '\0')
1769       {
1770         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1771           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1772         if (buffer[end_offset +2] != '\0')
1773         {
1774           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1775             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1776         }
1777       }
1778     }
1779 
1780     DestroyWindow(hwndRichEdit);
1781     hwndRichEdit = NULL;
1782   }
1783 
1784   /* Test detection of URLs within normal text - WM_CHAR case. */
1785   /* Test only the first two URL examples for brevity */
1786   for (i = 0; i < 2; i++) {
1787     hwndRichEdit = new_richedit(parent);
1788 
1789     /* Also for brevity, test only the first three delimiters */
1790     for (j = 0; j < 3; j++) {
1791       char * at_pos;
1792       int at_offset;
1793       int end_offset;
1794       int u, v;
1795 
1796       at_pos = strchr(templates_delim[j], 'X');
1797       at_offset = at_pos - templates_delim[j];
1798       end_offset = at_offset + strlen(urls[i].text);
1799 
1800       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1801       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1802       for (u = 0; templates_delim[j][u]; u++) {
1803         if (templates_delim[j][u] == '\r') {
1804           simulate_typing_characters(hwndRichEdit, "\r");
1805         } else if (templates_delim[j][u] != 'X') {
1806           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1807         } else {
1808           for (v = 0; urls[i].text[v]; v++) {
1809             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1810           }
1811         }
1812       }
1813       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1814 
1815       /* This assumes no templates start with the URL itself, and that they
1816          have at least two characters before the URL text */
1817       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1818         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1819       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1820         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1821       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1822         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1823 
1824       if (urls[i].is_url)
1825       {
1826         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1827           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1828         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1829           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1830       }
1831       else
1832       {
1833         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1834           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1835         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1836           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1837       }
1838       if (buffer[end_offset] != '\0')
1839       {
1840         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1841           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1842         if (buffer[end_offset +1] != '\0')
1843         {
1844           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1845             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1846         }
1847       }
1848 
1849       /* The following will insert a paragraph break after the first character
1850          of the URL candidate, thus breaking the URL. It is expected that the
1851          CFE_LINK attribute should break across both pieces of the URL */
1852       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1853       simulate_typing_characters(hwndRichEdit, "\r");
1854       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1855 
1856       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1857         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1858       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1859         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1860       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1861         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1862 
1863       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1864         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1865       /* end_offset moved because of paragraph break */
1866       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1867         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1868       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1869       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
1870       {
1871         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1872           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1873         if (buffer[end_offset +2] != '\0')
1874         {
1875           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1876             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1877         }
1878       }
1879 
1880       /* The following will remove the just-inserted paragraph break, thus
1881          restoring the URL */
1882       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1883       simulate_typing_characters(hwndRichEdit, "\b");
1884       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1885 
1886       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1887         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1888       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1889         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1890       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1891         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1892 
1893       if (urls[i].is_url)
1894       {
1895         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1896           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1897         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1898           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1899       }
1900       else
1901       {
1902         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1903           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1904         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1905           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1906       }
1907       if (buffer[end_offset] != '\0')
1908       {
1909         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1910           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1911         if (buffer[end_offset +1] != '\0')
1912         {
1913           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1914             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1915         }
1916       }
1917     }
1918     DestroyWindow(hwndRichEdit);
1919     hwndRichEdit = NULL;
1920   }
1921 
1922   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1923   /* Test just the first two URL examples for brevity */
1924   for (i = 0; i < 2; i++) {
1925     SETTEXTEX st;
1926 
1927     hwndRichEdit = new_richedit(parent);
1928 
1929     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1930        be detected:
1931        1) Set entire text, a la WM_SETTEXT
1932        2) Set a selection of the text to the URL
1933        3) Set a portion of the text at a time, which eventually results in
1934           an URL
1935        All of them should give equivalent results
1936      */
1937 
1938     /* Set entire text in one go, like WM_SETTEXT */
1939     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1940       char * at_pos;
1941       int at_offset;
1942       int end_offset;
1943 
1944       st.codepage = CP_ACP;
1945       st.flags = ST_DEFAULT;
1946 
1947       at_pos = strchr(templates_delim[j], 'X');
1948       at_offset = at_pos - templates_delim[j];
1949       strncpy(buffer, templates_delim[j], at_offset);
1950       buffer[at_offset] = '\0';
1951       strcat(buffer, urls[i].text);
1952       strcat(buffer, templates_delim[j] + at_offset + 1);
1953       end_offset = at_offset + strlen(urls[i].text);
1954 
1955       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1956       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1957 
1958       /* This assumes no templates start with the URL itself, and that they
1959          have at least two characters before the URL text */
1960       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1961         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1962       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1963         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1964       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1965         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1966 
1967       if (urls[i].is_url)
1968       {
1969         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1970           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1971         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1972           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1973       }
1974       else
1975       {
1976         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1977           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1978         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1979           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1980       }
1981       if (buffer[end_offset] != '\0')
1982       {
1983         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1984           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1985         if (buffer[end_offset +1] != '\0')
1986         {
1987           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1988             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1989         }
1990       }
1991     }
1992 
1993     /* Set selection with X to the URL */
1994     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1995       char * at_pos;
1996       int at_offset;
1997       int end_offset;
1998 
1999       at_pos = strchr(templates_delim[j], 'X');
2000       at_offset = at_pos - templates_delim[j];
2001       end_offset = at_offset + strlen(urls[i].text);
2002 
2003       st.codepage = CP_ACP;
2004       st.flags = ST_DEFAULT;
2005       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2006       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2007       st.flags = ST_SELECTION;
2008       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2009       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2010       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2011 
2012       /* This assumes no templates start with the URL itself, and that they
2013          have at least two characters before the URL text */
2014       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2015         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2016       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2017         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2018       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2019         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2020 
2021       if (urls[i].is_url)
2022       {
2023         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2024           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2025         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2026           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2027       }
2028       else
2029       {
2030         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2031           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2032         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2033           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2034       }
2035       if (buffer[end_offset] != '\0')
2036       {
2037         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2038           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2039         if (buffer[end_offset +1] != '\0')
2040         {
2041           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2042             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2043         }
2044       }
2045     }
2046 
2047     /* Set selection with X to the first character of the URL, then the rest */
2048     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2049       char * at_pos;
2050       int at_offset;
2051       int end_offset;
2052 
2053       at_pos = strchr(templates_delim[j], 'X');
2054       at_offset = at_pos - templates_delim[j];
2055       end_offset = at_offset + strlen(urls[i].text);
2056 
2057       strcpy(buffer, "YY");
2058       buffer[0] = urls[i].text[0];
2059 
2060       st.codepage = CP_ACP;
2061       st.flags = ST_DEFAULT;
2062       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2063       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2064       st.flags = ST_SELECTION;
2065       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2066       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2067       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2068       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2069       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2070 
2071       /* This assumes no templates start with the URL itself, and that they
2072          have at least two characters before the URL text */
2073       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2074         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2075       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2076         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2077       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2078         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2079 
2080       if (urls[i].is_url)
2081       {
2082         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2083           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2084         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2085           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2086       }
2087       else
2088       {
2089         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2090           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2091         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2092           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2093       }
2094       if (buffer[end_offset] != '\0')
2095       {
2096         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2097           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2098         if (buffer[end_offset +1] != '\0')
2099         {
2100           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2101             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2102         }
2103       }
2104     }
2105 
2106     DestroyWindow(hwndRichEdit);
2107     hwndRichEdit = NULL;
2108   }
2109 
2110   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2111   /* Test just the first two URL examples for brevity */
2112   for (i = 0; i < 2; i++) {
2113     hwndRichEdit = new_richedit(parent);
2114 
2115     /* Set selection with X to the URL */
2116     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2117       char * at_pos;
2118       int at_offset;
2119       int end_offset;
2120 
2121       at_pos = strchr(templates_delim[j], 'X');
2122       at_offset = at_pos - templates_delim[j];
2123       end_offset = at_offset + strlen(urls[i].text);
2124 
2125       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2126       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2127       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2128       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2129       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2130 
2131       /* This assumes no templates start with the URL itself, and that they
2132          have at least two characters before the URL text */
2133       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2134         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2135       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2136         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2137       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2138         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2139 
2140       if (urls[i].is_url)
2141       {
2142         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2143           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2144         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2145           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2146       }
2147       else
2148       {
2149         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2150           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2151         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2152           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2153       }
2154       if (buffer[end_offset] != '\0')
2155       {
2156         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2157           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2158         if (buffer[end_offset +1] != '\0')
2159         {
2160           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2161             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2162         }
2163       }
2164     }
2165 
2166     /* Set selection with X to the first character of the URL, then the rest */
2167     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2168       char * at_pos;
2169       int at_offset;
2170       int end_offset;
2171 
2172       at_pos = strchr(templates_delim[j], 'X');
2173       at_offset = at_pos - templates_delim[j];
2174       end_offset = at_offset + strlen(urls[i].text);
2175 
2176       strcpy(buffer, "YY");
2177       buffer[0] = urls[i].text[0];
2178 
2179       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2180       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2181       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2182       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2183       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2184       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2185       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2186 
2187       /* This assumes no templates start with the URL itself, and that they
2188          have at least two characters before the URL text */
2189       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2190         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2191       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2192         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2193       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2194         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2195 
2196       if (urls[i].is_url)
2197       {
2198         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2199           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2200         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2201           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2202       }
2203       else
2204       {
2205         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2206           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2207         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2208           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2209       }
2210       if (buffer[end_offset] != '\0')
2211       {
2212         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2213           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2214         if (buffer[end_offset +1] != '\0')
2215         {
2216           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2217             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2218         }
2219       }
2220     }
2221 
2222     DestroyWindow(hwndRichEdit);
2223     hwndRichEdit = NULL;
2224   }
2225 
2226   DestroyWindow(parent);
2227 }
2228 
2229 static void test_EM_SCROLL(void)
2230 {
2231   int i, j;
2232   int r; /* return value */
2233   int expr; /* expected return value */
2234   HWND hwndRichEdit = new_richedit(NULL);
2235   int y_before, y_after; /* units of lines of text */
2236 
2237   /* test a richedit box containing a single line of text */
2238   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2239   expr = 0x00010000;
2240   for (i = 0; i < 4; i++) {
2241     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2242 
2243     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2244     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2245     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2246        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2247     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2248        "(i == %d)\n", y_after, i);
2249   }
2250 
2251   /*
2252    * test a richedit box that will scroll. There are two general
2253    * cases: the case without any long lines and the case with a long
2254    * line.
2255    */
2256   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2257     if (i == 0)
2258       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2259     else
2260       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2261                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2262                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2263                   "LONG LINE \nb\nc\nd\ne");
2264     for (j = 0; j < 12; j++) /* reset scroll position to top */
2265       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2266 
2267     /* get first visible line */
2268     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2269     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2270 
2271     /* get new current first visible line */
2272     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2273 
2274     ok(((r & 0xffffff00) == 0x00010000) &&
2275        ((r & 0x000000ff) != 0x00000000),
2276        "EM_SCROLL page down didn't scroll by a small positive number of "
2277        "lines (r == 0x%08x)\n", r);
2278     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2279        "(line %d scrolled to line %d\n", y_before, y_after);
2280 
2281     y_before = y_after;
2282     
2283     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2284     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2285     ok(((r & 0xffffff00) == 0x0001ff00),
2286        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2287        "(r == 0x%08x)\n", r);
2288     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2289        "%d scrolled to line %d\n", y_before, y_after);
2290     
2291     y_before = y_after;
2292 
2293     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2294 
2295     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2296 
2297     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2298        "(r == 0x%08x)\n", r);
2299     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2300        "1 line (%d scrolled to %d)\n", y_before, y_after);
2301 
2302     y_before = y_after;
2303 
2304     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2305 
2306     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2307 
2308     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2309        "(r == 0x%08x)\n", r);
2310     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2311        "line (%d scrolled to %d)\n", y_before, y_after);
2312 
2313     y_before = y_after;
2314 
2315     r = SendMessage(hwndRichEdit, EM_SCROLL,
2316                     SB_LINEUP, 0); /* lineup beyond top */
2317 
2318     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2319 
2320     ok(r == 0x00010000,
2321        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2322     ok(y_before == y_after,
2323        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2324 
2325     y_before = y_after;
2326 
2327     r = SendMessage(hwndRichEdit, EM_SCROLL,
2328                     SB_PAGEUP, 0);/*page up beyond top */
2329 
2330     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2331 
2332     ok(r == 0x00010000,
2333        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2334     ok(y_before == y_after,
2335        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2336 
2337     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2338       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2339     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2340     r = SendMessage(hwndRichEdit, EM_SCROLL,
2341                     SB_PAGEDOWN, 0); /* page down beyond bot */
2342     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2343 
2344     ok(r == 0x00010000,
2345        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2346     ok(y_before == y_after,
2347        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2348        y_before, y_after);
2349 
2350     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2351     SendMessage(hwndRichEdit, EM_SCROLL,
2352                 SB_LINEDOWN, 0); /* line down beyond bot */
2353     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2354     
2355     ok(r == 0x00010000,
2356        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2357     ok(y_before == y_after,
2358        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2359        y_before, y_after);
2360   }
2361   DestroyWindow(hwndRichEdit);
2362 }
2363 
2364 unsigned int recursionLevel = 0;
2365 unsigned int WM_SIZE_recursionLevel = 0;
2366 BOOL bailedOutOfRecursion = FALSE;
2367 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2368 
2369 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2370 {
2371     LRESULT r;
2372 
2373     if (bailedOutOfRecursion) return 0;
2374     if (recursionLevel >= 32) {
2375         bailedOutOfRecursion = TRUE;
2376         return 0;
2377     }
2378 
2379     recursionLevel++;
2380     switch (message) {
2381     case WM_SIZE:
2382         WM_SIZE_recursionLevel++;
2383         r = richeditProc(hwnd, message, wParam, lParam);
2384         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2385         ShowScrollBar(hwnd, SB_VERT, TRUE);
2386         WM_SIZE_recursionLevel--;
2387         break;
2388     default:
2389         r = richeditProc(hwnd, message, wParam, lParam);
2390         break;
2391     }
2392     recursionLevel--;
2393     return r;
2394 }
2395 
2396 static void test_scrollbar_visibility(void)
2397 {
2398   HWND hwndRichEdit;
2399   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2400   SCROLLINFO si;
2401   WNDCLASSA cls;
2402   BOOL r;
2403 
2404   /* These tests show that richedit should temporarily refrain from automatically
2405      hiding or showing its scrollbars (vertical at least) when an explicit request
2406      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2407      Some applications depend on forced showing (when otherwise richedit would
2408      hide the vertical scrollbar) and are thrown on an endless recursive loop
2409      if richedit auto-hides the scrollbar again. Apparently they never heard of
2410      the ES_DISABLENOSCROLL style... */
2411 
2412   hwndRichEdit = new_richedit(NULL);
2413 
2414   /* Test default scrollbar visibility behavior */
2415   memset(&si, 0, sizeof(si));
2416   si.cbSize = sizeof(si);
2417   si.fMask = SIF_PAGE | SIF_RANGE;
2418   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2419   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2420     "Vertical scrollbar is visible, should be invisible.\n");
2421   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2422         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2423         si.nPage, si.nMin, si.nMax);
2424 
2425   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2426   memset(&si, 0, sizeof(si));
2427   si.cbSize = sizeof(si);
2428   si.fMask = SIF_PAGE | SIF_RANGE;
2429   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2430   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2431     "Vertical scrollbar is visible, should be invisible.\n");
2432   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2433         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2434         si.nPage, si.nMin, si.nMax);
2435 
2436   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2437   memset(&si, 0, sizeof(si));
2438   si.cbSize = sizeof(si);
2439   si.fMask = SIF_PAGE | SIF_RANGE;
2440   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2441   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2442     "Vertical scrollbar is invisible, should be visible.\n");
2443   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2444         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2445         si.nPage, si.nMin, si.nMax);
2446 
2447   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2448      even though it hides the scrollbar */
2449   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2450   memset(&si, 0, sizeof(si));
2451   si.cbSize = sizeof(si);
2452   si.fMask = SIF_PAGE | SIF_RANGE;
2453   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2454   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2455     "Vertical scrollbar is visible, should be invisible.\n");
2456   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2457         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2458         si.nPage, si.nMin, si.nMax);
2459 
2460   /* Setting non-scrolling text again does *not* reset scrollbar range */
2461   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2462   memset(&si, 0, sizeof(si));
2463   si.cbSize = sizeof(si);
2464   si.fMask = SIF_PAGE | SIF_RANGE;
2465   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2466   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2467     "Vertical scrollbar is visible, should be invisible.\n");
2468   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2469         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2470         si.nPage, si.nMin, si.nMax);
2471 
2472   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2473   memset(&si, 0, sizeof(si));
2474   si.cbSize = sizeof(si);
2475   si.fMask = SIF_PAGE | SIF_RANGE;
2476   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2477   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2478     "Vertical scrollbar is visible, should be invisible.\n");
2479   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2480         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2481         si.nPage, si.nMin, si.nMax);
2482 
2483   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2484   memset(&si, 0, sizeof(si));
2485   si.cbSize = sizeof(si);
2486   si.fMask = SIF_PAGE | SIF_RANGE;
2487   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2488   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2489     "Vertical scrollbar is visible, should be invisible.\n");
2490   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2491         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2492         si.nPage, si.nMin, si.nMax);
2493 
2494   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2495   memset(&si, 0, sizeof(si));
2496   si.cbSize = sizeof(si);
2497   si.fMask = SIF_PAGE | SIF_RANGE;
2498   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2499   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2500     "Vertical scrollbar is visible, should be invisible.\n");
2501   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2502         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2503         si.nPage, si.nMin, si.nMax);
2504 
2505   DestroyWindow(hwndRichEdit);
2506 
2507   /* Test again, with ES_DISABLENOSCROLL style */
2508   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2509 
2510   /* Test default scrollbar visibility behavior */
2511   memset(&si, 0, sizeof(si));
2512   si.cbSize = sizeof(si);
2513   si.fMask = SIF_PAGE | SIF_RANGE;
2514   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2515   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2516     "Vertical scrollbar is invisible, should be visible.\n");
2517   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2518         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2519         si.nPage, si.nMin, si.nMax);
2520 
2521   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2522   memset(&si, 0, sizeof(si));
2523   si.cbSize = sizeof(si);
2524   si.fMask = SIF_PAGE | SIF_RANGE;
2525   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2526   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2527     "Vertical scrollbar is invisible, should be visible.\n");
2528   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2529         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2530         si.nPage, si.nMin, si.nMax);
2531 
2532   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2533   memset(&si, 0, sizeof(si));
2534   si.cbSize = sizeof(si);
2535   si.fMask = SIF_PAGE | SIF_RANGE;
2536   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2537   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2538     "Vertical scrollbar is invisible, should be visible.\n");
2539   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2540         "reported page/range is %d (%d..%d)\n",
2541         si.nPage, si.nMin, si.nMax);
2542 
2543   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2544   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2545   memset(&si, 0, sizeof(si));
2546   si.cbSize = sizeof(si);
2547   si.fMask = SIF_PAGE | SIF_RANGE;
2548   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2549   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2550     "Vertical scrollbar is invisible, should be visible.\n");
2551   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2552         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2553         si.nPage, si.nMin, si.nMax);
2554 
2555   /* Setting non-scrolling text again does *not* reset scrollbar range */
2556   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2557   memset(&si, 0, sizeof(si));
2558   si.cbSize = sizeof(si);
2559   si.fMask = SIF_PAGE | SIF_RANGE;
2560   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2561   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2562     "Vertical scrollbar is invisible, should be visible.\n");
2563   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2564         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2565         si.nPage, si.nMin, si.nMax);
2566 
2567   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2568   memset(&si, 0, sizeof(si));
2569   si.cbSize = sizeof(si);
2570   si.fMask = SIF_PAGE | SIF_RANGE;
2571   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2572   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2573     "Vertical scrollbar is invisible, should be visible.\n");
2574   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2575         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2576         si.nPage, si.nMin, si.nMax);
2577 
2578   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2579   memset(&si, 0, sizeof(si));
2580   si.cbSize = sizeof(si);
2581   si.fMask = SIF_PAGE | SIF_RANGE;
2582   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2583   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2584     "Vertical scrollbar is invisible, should be visible.\n");
2585   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2586         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2587         si.nPage, si.nMin, si.nMax);
2588 
2589   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2590   memset(&si, 0, sizeof(si));
2591   si.cbSize = sizeof(si);
2592   si.fMask = SIF_PAGE | SIF_RANGE;
2593   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595     "Vertical scrollbar is invisible, should be visible.\n");
2596   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2597         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2598         si.nPage, si.nMin, si.nMax);
2599 
2600   DestroyWindow(hwndRichEdit);
2601 
2602   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2603   hwndRichEdit = new_richedit(NULL);
2604 
2605   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2606   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2607   memset(&si, 0, sizeof(si));
2608   si.cbSize = sizeof(si);
2609   si.fMask = SIF_PAGE | SIF_RANGE;
2610   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2611   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2612     "Vertical scrollbar is invisible, should be visible.\n");
2613   todo_wine {
2614   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2615         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2616         si.nPage, si.nMin, si.nMax);
2617   }
2618 
2619   /* Ditto, see above */
2620   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2621   memset(&si, 0, sizeof(si));
2622   si.cbSize = sizeof(si);
2623   si.fMask = SIF_PAGE | SIF_RANGE;
2624   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2625   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2626     "Vertical scrollbar is invisible, should be visible.\n");
2627   todo_wine {
2628   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2629         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2630         si.nPage, si.nMin, si.nMax);
2631   }
2632 
2633   /* Ditto, see above */
2634   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2635   memset(&si, 0, sizeof(si));
2636   si.cbSize = sizeof(si);
2637   si.fMask = SIF_PAGE | SIF_RANGE;
2638   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2640     "Vertical scrollbar is invisible, should be visible.\n");
2641   todo_wine {
2642   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2643         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2644         si.nPage, si.nMin, si.nMax);
2645   }
2646 
2647   /* Ditto, see above */
2648   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2649   memset(&si, 0, sizeof(si));
2650   si.cbSize = sizeof(si);
2651   si.fMask = SIF_PAGE | SIF_RANGE;
2652   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2653   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2654     "Vertical scrollbar is invisible, should be visible.\n");
2655   todo_wine {
2656   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2657         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2658         si.nPage, si.nMin, si.nMax);
2659   }
2660 
2661   /* Ditto, see above */
2662   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2663   memset(&si, 0, sizeof(si));
2664   si.cbSize = sizeof(si);
2665   si.fMask = SIF_PAGE | SIF_RANGE;
2666   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2667   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2668     "Vertical scrollbar is invisible, should be visible.\n");
2669   todo_wine {
2670   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2671         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2672         si.nPage, si.nMin, si.nMax);
2673   }
2674 
2675   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2676   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2677   memset(&si, 0, sizeof(si));
2678   si.cbSize = sizeof(si);
2679   si.fMask = SIF_PAGE | SIF_RANGE;
2680   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2681   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2682     "Vertical scrollbar is visible, should be invisible.\n");
2683   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2684         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2685         si.nPage, si.nMin, si.nMax);
2686 
2687   DestroyWindow(hwndRichEdit);
2688 
2689   hwndRichEdit = new_richedit(NULL);
2690 
2691   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2692   memset(&si, 0, sizeof(si));
2693   si.cbSize = sizeof(si);
2694   si.fMask = SIF_PAGE | SIF_RANGE;
2695   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2697     "Vertical scrollbar is visible, should be invisible.\n");
2698   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2699         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2700         si.nPage, si.nMin, si.nMax);
2701 
2702   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2703   memset(&si, 0, sizeof(si));
2704   si.cbSize = sizeof(si);
2705   si.fMask = SIF_PAGE | SIF_RANGE;
2706   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2707   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2708     "Vertical scrollbar is visible, should be invisible.\n");
2709   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2710         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2711         si.nPage, si.nMin, si.nMax);
2712 
2713   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714   memset(&si, 0, sizeof(si));
2715   si.cbSize = sizeof(si);
2716   si.fMask = SIF_PAGE | SIF_RANGE;
2717   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2719     "Vertical scrollbar is visible, should be invisible.\n");
2720   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2721         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2722         si.nPage, si.nMin, si.nMax);
2723 
2724   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2725   memset(&si, 0, sizeof(si));
2726   si.cbSize = sizeof(si);
2727   si.fMask = SIF_PAGE | SIF_RANGE;
2728   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2729   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2730     "Vertical scrollbar is visible, should be invisible.\n");
2731   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2732         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2733         si.nPage, si.nMin, si.nMax);
2734 
2735   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2736   memset(&si, 0, sizeof(si));
2737   si.cbSize = sizeof(si);
2738   si.fMask = SIF_PAGE | SIF_RANGE;
2739   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2740   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2741     "Vertical scrollbar is invisible, should be visible.\n");
2742   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2743         "reported page/range is %d (%d..%d)\n",
2744         si.nPage, si.nMin, si.nMax);
2745 
2746   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2747   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2748   memset(&si, 0, sizeof(si));
2749   si.cbSize = sizeof(si);
2750   si.fMask = SIF_PAGE | SIF_RANGE;
2751   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2752   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2753     "Vertical scrollbar is visible, should be invisible.\n");
2754   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2755         "reported page/range is %d (%d..%d)\n",
2756         si.nPage, si.nMin, si.nMax);
2757 
2758   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2759   memset(&si, 0, sizeof(si));
2760   si.cbSize = sizeof(si);
2761   si.fMask = SIF_PAGE | SIF_RANGE;
2762   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2763   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2764     "Vertical scrollbar is visible, should be invisible.\n");
2765   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2766         "reported page/range is %d (%d..%d)\n",
2767         si.nPage, si.nMin, si.nMax);
2768 
2769   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2770      EM_SCROLL will make visible any forcefully invisible scrollbar */
2771   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2772   memset(&si, 0, sizeof(si));
2773   si.cbSize = sizeof(si);
2774   si.fMask = SIF_PAGE | SIF_RANGE;
2775   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2776   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2777     "Vertical scrollbar is invisible, should be visible.\n");
2778   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2779         "reported page/range is %d (%d..%d)\n",
2780         si.nPage, si.nMin, si.nMax);
2781 
2782   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2783   memset(&si, 0, sizeof(si));
2784   si.cbSize = sizeof(si);
2785   si.fMask = SIF_PAGE | SIF_RANGE;
2786   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788     "Vertical scrollbar is visible, should be invisible.\n");
2789   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2790         "reported page/range is %d (%d..%d)\n",
2791         si.nPage, si.nMin, si.nMax);
2792 
2793   /* Again, EM_SCROLL, with SB_LINEUP */
2794   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2795   memset(&si, 0, sizeof(si));
2796   si.cbSize = sizeof(si);
2797   si.fMask = SIF_PAGE | SIF_RANGE;
2798   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2799   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2800     "Vertical scrollbar is invisible, should be visible.\n");
2801   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2802         "reported page/range is %d (%d..%d)\n",
2803         si.nPage, si.nMin, si.nMax);
2804 
2805   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2806   memset(&si, 0, sizeof(si));
2807   si.cbSize = sizeof(si);
2808   si.fMask = SIF_PAGE | SIF_RANGE;
2809   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2810   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2811     "Vertical scrollbar is visible, should be invisible.\n");
2812   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2813         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2814         si.nPage, si.nMin, si.nMax);
2815 
2816   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2817   memset(&si, 0, sizeof(si));
2818   si.cbSize = sizeof(si);
2819   si.fMask = SIF_PAGE | SIF_RANGE;
2820   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2821   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2822     "Vertical scrollbar is invisible, should be visible.\n");
2823   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2824         "reported page/range is %d (%d..%d)\n",
2825         si.nPage, si.nMin, si.nMax);
2826 
2827   DestroyWindow(hwndRichEdit);
2828 
2829 
2830   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2831   hwndRichEdit = new_richedit(NULL);
2832 
2833 #define ENABLE_WS_VSCROLL(hwnd) \
2834     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2835 #define DISABLE_WS_VSCROLL(hwnd) \
2836     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2837 
2838   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2839   ENABLE_WS_VSCROLL(hwndRichEdit);
2840   memset(&si, 0, sizeof(si));
2841   si.cbSize = sizeof(si);
2842   si.fMask = SIF_PAGE | SIF_RANGE;
2843   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2845     "Vertical scrollbar is invisible, should be visible.\n");
2846   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2847         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2848         si.nPage, si.nMin, si.nMax);
2849 
2850   /* Ditto, see above */
2851   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2852   memset(&si, 0, sizeof(si));
2853   si.cbSize = sizeof(si);
2854   si.fMask = SIF_PAGE | SIF_RANGE;
2855   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2856   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2857     "Vertical scrollbar is invisible, should be visible.\n");
2858   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2859         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2860         si.nPage, si.nMin, si.nMax);
2861 
2862   /* Ditto, see above */
2863   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2864   memset(&si, 0, sizeof(si));
2865   si.cbSize = sizeof(si);
2866   si.fMask = SIF_PAGE | SIF_RANGE;
2867   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2868   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2869     "Vertical scrollbar is invisible, should be visible.\n");
2870   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2871         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2872         si.nPage, si.nMin, si.nMax);
2873 
2874   /* Ditto, see above */
2875   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2876   memset(&si, 0, sizeof(si));
2877   si.cbSize = sizeof(si);
2878   si.fMask = SIF_PAGE | SIF_RANGE;
2879   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2881     "Vertical scrollbar is invisible, should be visible.\n");
2882   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884         si.nPage, si.nMin, si.nMax);
2885 
2886   /* Ditto, see above */
2887   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2888   memset(&si, 0, sizeof(si));
2889   si.cbSize = sizeof(si);
2890   si.fMask = SIF_PAGE | SIF_RANGE;
2891   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2892   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2893     "Vertical scrollbar is invisible, should be visible.\n");
2894   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2895         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2896         si.nPage, si.nMin, si.nMax);
2897 
2898   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2899   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2900   memset(&si, 0, sizeof(si));
2901   si.cbSize = sizeof(si);
2902   si.fMask = SIF_PAGE | SIF_RANGE;
2903   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2904   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2905     "Vertical scrollbar is visible, should be invisible.\n");
2906   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2907         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2908         si.nPage, si.nMin, si.nMax);
2909 
2910   DestroyWindow(hwndRichEdit);
2911 
2912   hwndRichEdit = new_richedit(NULL);
2913 
2914   DISABLE_WS_VSCROLL(hwndRichEdit);
2915   memset(&si, 0, sizeof(si));
2916   si.cbSize = sizeof(si);
2917   si.fMask = SIF_PAGE | SIF_RANGE;
2918   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2920     "Vertical scrollbar is visible, should be invisible.\n");
2921   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923         si.nPage, si.nMin, si.nMax);
2924 
2925   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2926   memset(&si, 0, sizeof(si));
2927   si.cbSize = sizeof(si);
2928   si.fMask = SIF_PAGE | SIF_RANGE;
2929   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931     "Vertical scrollbar is visible, should be invisible.\n");
2932   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934         si.nPage, si.nMin, si.nMax);
2935 
2936   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2937   memset(&si, 0, sizeof(si));
2938   si.cbSize = sizeof(si);
2939   si.fMask = SIF_PAGE | SIF_RANGE;
2940   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2942     "Vertical scrollbar is visible, should be invisible.\n");
2943   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2944         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2945         si.nPage, si.nMin, si.nMax);
2946 
2947   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2948   memset(&si, 0, sizeof(si));
2949   si.cbSize = sizeof(si);
2950   si.fMask = SIF_PAGE | SIF_RANGE;
2951   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2952   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2953     "Vertical scrollbar is visible, should be invisible.\n");
2954   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2955         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2956         si.nPage, si.nMin, si.nMax);
2957 
2958   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2959   memset(&si, 0, sizeof(si));
2960   si.cbSize = sizeof(si);
2961   si.fMask = SIF_PAGE | SIF_RANGE;
2962   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2963   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2964     "Vertical scrollbar is invisible, should be visible.\n");
2965   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2966         "reported page/range is %d (%d..%d)\n",
2967         si.nPage, si.nMin, si.nMax);
2968 
2969   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2970   DISABLE_WS_VSCROLL(hwndRichEdit);
2971   memset(&si, 0, sizeof(si));
2972   si.cbSize = sizeof(si);
2973   si.fMask = SIF_PAGE | SIF_RANGE;
2974   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2975   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),