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