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