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

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

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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