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