~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/riched20/tests/editor.c

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