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