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