~ [ 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_SETPARAFORMAT(void)
1091 {
1092   HWND hwndRichEdit = new_richedit(NULL);
1093   PARAFORMAT2 fmt;
1094   HRESULT ret;
1095   fmt.cbSize = sizeof(PARAFORMAT2);
1096   fmt.dwMask = PFM_ALIGNMENT;
1097   fmt.wAlignment = PFA_LEFT;
1098 
1099   ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1100   ok(ret != 0, "expected non-zero got %d\n", ret);
1101 
1102   fmt.cbSize = sizeof(PARAFORMAT2);
1103   fmt.dwMask = -1;
1104   ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1105   ok(ret == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, ret);
1106   ok(fmt.dwMask == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, fmt.dwMask);
1107 
1108   DestroyWindow(hwndRichEdit);
1109 }
1110 
1111 static void test_TM_PLAINTEXT(void)
1112 {
1113   /*Tests plain text properties*/
1114 
1115   HWND hwndRichEdit = new_richedit(NULL);
1116   CHARFORMAT2 cf2, cf2test;
1117   CHARRANGE cr;
1118   int rc = 0;
1119 
1120   /*Switch to plain text mode*/
1121 
1122   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1123   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1124 
1125   /*Fill control with text*/
1126 
1127   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1128 
1129   /*Select some text and bold it*/
1130 
1131   cr.cpMin = 10;
1132   cr.cpMax = 20;
1133   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1134   cf2.cbSize = sizeof(CHARFORMAT2);
1135   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1136               (LPARAM) &cf2);
1137 
1138   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1139   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1140 
1141   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1142   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1143 
1144   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1145   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1146 
1147   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1148   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1149 
1150   /*Get the formatting of those characters*/
1151 
1152   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1153 
1154   /*Get the formatting of some other characters*/
1155   cf2test.cbSize = sizeof(CHARFORMAT2);
1156   cr.cpMin = 21;
1157   cr.cpMax = 30;
1158   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1159   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1160 
1161   /*Test that they are the same as plain text allows only one formatting*/
1162 
1163   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1164      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1165      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1166   
1167   /*Fill the control with a "wine" string, which when inserted will be bold*/
1168 
1169   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1170 
1171   /*Copy the bolded "wine" string*/
1172 
1173   cr.cpMin = 0;
1174   cr.cpMax = 4;
1175   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1176   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1177 
1178   /*Swap back to rich text*/
1179 
1180   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1181   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1182 
1183   /*Set the default formatting to bold italics*/
1184 
1185   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1186   cf2.dwMask |= CFM_ITALIC;
1187   cf2.dwEffects ^= CFE_ITALIC;
1188   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1189   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1190 
1191   /*Set the text in the control to "wine", which will be bold and italicized*/
1192 
1193   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1194 
1195   /*Paste the plain text "wine" string, which should take the insert
1196    formatting, which at the moment is bold italics*/
1197 
1198   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1199 
1200   /*Select the first "wine" string and retrieve its formatting*/
1201 
1202   cr.cpMin = 1;
1203   cr.cpMax = 3;
1204   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1205   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1206 
1207   /*Select the second "wine" string and retrieve its formatting*/
1208 
1209   cr.cpMin = 5;
1210   cr.cpMax = 7;
1211   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1212   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1213 
1214   /*Compare the two formattings. They should be the same.*/
1215 
1216   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1217      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1218      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1219   DestroyWindow(hwndRichEdit);
1220 }
1221 
1222 static void test_WM_GETTEXT(void)
1223 {
1224     HWND hwndRichEdit = new_richedit(NULL);
1225     static const char text[] = "Hello. My name is RichEdit!";
1226     static const char text2[] = "Hello. My name is RichEdit!\r";
1227     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1228     char buffer[1024] = {0};
1229     int result;
1230 
1231     /* Baseline test with normal-sized buffer */
1232     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1233     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1234     ok(result == lstrlen(buffer),
1235         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1236     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1237     result = strcmp(buffer,text);
1238     ok(result == 0, 
1239         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1240 
1241     /* Test for returned value of WM_GETTEXTLENGTH */
1242     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1243     ok(result == lstrlen(text),
1244         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1245         result, lstrlen(text));
1246 
1247     /* Test for behavior in overflow case */
1248     memset(buffer, 0, 1024);
1249     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1250     ok(result == 0 ||
1251        result == lstrlenA(text) - 1, /* XP, win2k3 */
1252         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1253     result = strcmp(buffer,text);
1254     if (result)
1255         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1256     ok(result == 0,
1257         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1258 
1259     /* Baseline test with normal-sized buffer and carriage return */
1260     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1261     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1262     ok(result == lstrlen(buffer),
1263         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1264     result = strcmp(buffer,text2_after);
1265     ok(result == 0,
1266         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1267 
1268     /* Test for returned value of WM_GETTEXTLENGTH */
1269     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1270     ok(result == lstrlen(text2_after),
1271         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1272         result, lstrlen(text2_after));
1273 
1274     /* Test for behavior of CRLF conversion in case of overflow */
1275     memset(buffer, 0, 1024);
1276     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1277     ok(result == 0 ||
1278        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1279         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1280     result = strcmp(buffer,text2);
1281     if (result)
1282         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1283     ok(result == 0,
1284         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1285 
1286     DestroyWindow(hwndRichEdit);
1287 }
1288 
1289 static void test_EM_GETTEXTRANGE(void)
1290 {
1291     HWND hwndRichEdit = new_richedit(NULL);
1292     const char * text1 = "foo bar\r\nfoo bar";
1293     const char * text2 = "foo bar\rfoo bar";
1294     const char * expect = "bar\rfoo";
1295     char buffer[1024] = {0};
1296     LRESULT result;
1297     TEXTRANGEA textRange;
1298 
1299     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1300 
1301     textRange.lpstrText = buffer;
1302     textRange.chrg.cpMin = 4;
1303     textRange.chrg.cpMax = 11;
1304     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1305     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1306     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1307 
1308     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1309 
1310     textRange.lpstrText = buffer;
1311     textRange.chrg.cpMin = 4;
1312     textRange.chrg.cpMax = 11;
1313     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1314     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1315     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1316 
1317     DestroyWindow(hwndRichEdit);
1318 }
1319 
1320 static void test_EM_GETSELTEXT(void)
1321 {
1322     HWND hwndRichEdit = new_richedit(NULL);
1323     const char * text1 = "foo bar\r\nfoo bar";
1324     const char * text2 = "foo bar\rfoo bar";
1325     const char * expect = "bar\rfoo";
1326     char buffer[1024] = {0};
1327     LRESULT result;
1328 
1329     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1330 
1331     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1332     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1333     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1334     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1335 
1336     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1337 
1338     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1339     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1340     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1341     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1342 
1343     DestroyWindow(hwndRichEdit);
1344 }
1345 
1346 /* FIXME: need to test unimplemented options and robustly test wparam */
1347 static void test_EM_SETOPTIONS(void)
1348 {
1349     HWND hwndRichEdit = new_richedit(NULL);
1350     static const char text[] = "Hello. My name is RichEdit!";
1351     char buffer[1024] = {0};
1352 
1353     /* NEGATIVE TESTING - NO OPTIONS SET */
1354     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1355     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1356 
1357     /* testing no readonly by sending 'a' to the control*/
1358     SetFocus(hwndRichEdit);
1359     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1360     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1361     ok(buffer[0]=='a', 
1362        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1363     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1364 
1365     /* READONLY - sending 'a' to the control */
1366     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1367     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1368     SetFocus(hwndRichEdit);
1369     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1370     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1371     ok(buffer[0]==text[0], 
1372        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1373 
1374     DestroyWindow(hwndRichEdit);
1375 }
1376 
1377 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1378 {
1379   CHARFORMAT2W text_format;
1380   text_format.cbSize = sizeof(text_format);
1381   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1382   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1383   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1384 }
1385 
1386 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1387 {
1388   int link_present = 0;
1389 
1390   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1391   if (is_url) 
1392   { /* control text is url; should get CFE_LINK */
1393         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1394   }
1395   else 
1396   {
1397     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1398   }
1399 }
1400 
1401 static HWND new_static_wnd(HWND parent) {
1402   return new_window("Static", 0, parent);
1403 }
1404 
1405 static void test_EM_AUTOURLDETECT(void)
1406 {
1407   /* DO NOT change the properties of the first two elements. To shorten the
1408      tests, all tests after WM_SETTEXT test just the first two elements -
1409      one non-URL and one URL */
1410   struct urls_s {
1411     const char *text;
1412     int is_url;
1413   } urls[12] = {
1414     {"winehq.org", 0},
1415     {"http://www.winehq.org", 1},
1416     {"http//winehq.org", 0},
1417     {"ww.winehq.org", 0},
1418     {"www.winehq.org", 1},
1419     {"ftp://192.168.1.1", 1},
1420     {"ftp//192.168.1.1", 0},
1421     {"mailto:your@email.com", 1},    
1422     {"prospero:prosperoserver", 1},
1423     {"telnet:test", 1},
1424     {"news:newserver", 1},
1425     {"wais:waisserver", 1}  
1426   };
1427 
1428   int i, j;
1429   int urlRet=-1;
1430   HWND hwndRichEdit, parent;
1431 
1432   /* All of the following should cause the URL to be detected  */
1433   const char * templates_delim[] = {
1434     "This is some text with X on it",
1435     "This is some text with (X) on it",
1436     "This is some text with X\r on it",
1437     "This is some text with ---X--- on it",
1438     "This is some text with \"X\" on it",
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 
1443     "This text ends with X",
1444 
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 
1451     "This is some text with (X on it",
1452     "This is some text with \rX on it",
1453     "This is some text with ---X on it",
1454     "This is some text with \"X on it",
1455     "This is some text with 'X on it",
1456     "This is some text with :X on it",
1457   };
1458   /* None of these should cause the URL to be detected */
1459   const char * templates_non_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     "This is some text with |X on it",
1469     "This is some text with *X on it",
1470     "This is some text with /X on it",
1471     "This is some text with +X on it",
1472     "This is some text with %X on it",
1473     "This is some text with #X on it",
1474     "This is some text with @X on it",
1475     "This is some text with \\X on it",
1476   };
1477   /* All of these cause the URL detection to be extended by one more byte,
1478      thus demonstrating that the tested character is considered as part
1479      of the URL. */
1480   const char * templates_xten_delim[] = {
1481     "This is some text with X| on it",
1482     "This is some text with X* on it",
1483     "This is some text with X/ on it",
1484     "This is some text with X+ on it",
1485     "This is some text with X% on it",
1486     "This is some text with X# on it",
1487     "This is some text with X@ on it",
1488     "This is some text with X\\ on it",
1489   };
1490   char buffer[1024];
1491 
1492   parent = new_static_wnd(NULL);
1493   hwndRichEdit = new_richedit(parent);
1494   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1495   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1496   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1497   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1498   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1499   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1500   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1501   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1502   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1503   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1504   /* for each url, check the text to see if CFE_LINK effect is present */
1505   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1506 
1507     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1508     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1509     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1510 
1511     /* Link detection should happen immediately upon WM_SETTEXT */
1512     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1513     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1514     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1515   }
1516   DestroyWindow(hwndRichEdit);
1517 
1518   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1519   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1520     hwndRichEdit = new_richedit(parent);
1521 
1522     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1523       char * at_pos;
1524       int at_offset;
1525       int end_offset;
1526 
1527       at_pos = strchr(templates_delim[j], 'X');
1528       at_offset = at_pos - templates_delim[j];
1529       strncpy(buffer, templates_delim[j], at_offset);
1530       buffer[at_offset] = '\0';
1531       strcat(buffer, urls[i].text);
1532       strcat(buffer, templates_delim[j] + at_offset + 1);
1533       end_offset = at_offset + strlen(urls[i].text);
1534 
1535       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1536       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1537 
1538       /* This assumes no templates start with the URL itself, and that they
1539          have at least two characters before the URL text */
1540       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1541         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1542       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1543         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1544       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1545         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1546 
1547       if (urls[i].is_url)
1548       {
1549         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1550           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1551         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1552           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1553       }
1554       else
1555       {
1556         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1557           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1558         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1559           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1560       }
1561       if (buffer[end_offset] != '\0')
1562       {
1563         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1564           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1565         if (buffer[end_offset +1] != '\0')
1566         {
1567           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1568             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1569         }
1570       }
1571     }
1572 
1573     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1574       char * at_pos;
1575       int at_offset;
1576       int end_offset;
1577 
1578       at_pos = strchr(templates_non_delim[j], 'X');
1579       at_offset = at_pos - templates_non_delim[j];
1580       strncpy(buffer, templates_non_delim[j], at_offset);
1581       buffer[at_offset] = '\0';
1582       strcat(buffer, urls[i].text);
1583       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1584       end_offset = at_offset + strlen(urls[i].text);
1585 
1586       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1587       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1588 
1589       /* This assumes no templates start with the URL itself, and that they
1590          have at least two characters before the URL text */
1591       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1592         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1593       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1594         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1595       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1596         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1597 
1598       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1599         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1600       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1601         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1602       if (buffer[end_offset] != '\0')
1603       {
1604         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1605           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1606         if (buffer[end_offset +1] != '\0')
1607         {
1608           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1609             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1610         }
1611       }
1612     }
1613 
1614     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1615       char * at_pos;
1616       int at_offset;
1617       int end_offset;
1618 
1619       at_pos = strchr(templates_xten_delim[j], 'X');
1620       at_offset = at_pos - templates_xten_delim[j];
1621       strncpy(buffer, templates_xten_delim[j], at_offset);
1622       buffer[at_offset] = '\0';
1623       strcat(buffer, urls[i].text);
1624       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1625       end_offset = at_offset + strlen(urls[i].text);
1626 
1627       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1628       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1629 
1630       /* This assumes no templates start with the URL itself, and that they
1631          have at least two characters before the URL text */
1632       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1633         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1634       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1635         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1636       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1637         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1638 
1639       if (urls[i].is_url)
1640       {
1641         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1642           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1643         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1644           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1645         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1646           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1647       }
1648       else
1649       {
1650         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1651           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1652         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1653           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1654         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1655           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1656       }
1657       if (buffer[end_offset +1] != '\0')
1658       {
1659         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1660           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1661         if (buffer[end_offset +2] != '\0')
1662         {
1663           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1664             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1665         }
1666       }
1667     }
1668 
1669     DestroyWindow(hwndRichEdit);
1670     hwndRichEdit = NULL;
1671   }
1672 
1673   /* Test detection of URLs within normal text - WM_CHAR case. */
1674   /* Test only the first two URL examples for brevity */
1675   for (i = 0; i < 2; i++) {
1676     hwndRichEdit = new_richedit(parent);
1677 
1678     /* Also for brevity, test only the first three delimiters */
1679     for (j = 0; j < 3; j++) {
1680       char * at_pos;
1681       int at_offset;
1682       int end_offset;
1683       int u, v;
1684 
1685       at_pos = strchr(templates_delim[j], 'X');
1686       at_offset = at_pos - templates_delim[j];
1687       end_offset = at_offset + strlen(urls[i].text);
1688 
1689       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1690       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1691       for (u = 0; templates_delim[j][u]; u++) {
1692         if (templates_delim[j][u] == '\r') {
1693           simulate_typing_characters(hwndRichEdit, "\r");
1694         } else if (templates_delim[j][u] != 'X') {
1695           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1696         } else {
1697           for (v = 0; urls[i].text[v]; v++) {
1698             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1699           }
1700         }
1701       }
1702       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1703 
1704       /* This assumes no templates start with the URL itself, and that they
1705          have at least two characters before the URL text */
1706       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1707         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1708       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1709         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1710       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1711         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1712 
1713       if (urls[i].is_url)
1714       {
1715         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1716           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1717         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1718           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1719       }
1720       else
1721       {
1722         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1723           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1724         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1725           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1726       }
1727       if (buffer[end_offset] != '\0')
1728       {
1729         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1730           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1731         if (buffer[end_offset +1] != '\0')
1732         {
1733           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1734             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1735         }
1736       }
1737 
1738       /* The following will insert a paragraph break after the first character
1739          of the URL candidate, thus breaking the URL. It is expected that the
1740          CFE_LINK attribute should break across both pieces of the URL */
1741       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1742       simulate_typing_characters(hwndRichEdit, "\r");
1743       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1744 
1745       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1746         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1747       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1748         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1749       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1750         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1751 
1752       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1754       /* end_offset moved because of paragraph break */
1755       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1756         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1757       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1758       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
1759       {
1760         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1761           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1762         if (buffer[end_offset +2] != '\0')
1763         {
1764           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1765             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1766         }
1767       }
1768 
1769       /* The following will remove the just-inserted paragraph break, thus
1770          restoring the URL */
1771       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1772       simulate_typing_characters(hwndRichEdit, "\b");
1773       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1774 
1775       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1776         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1777       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1778         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1779       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1780         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1781 
1782       if (urls[i].is_url)
1783       {
1784         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1785           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1786         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1787           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1788       }
1789       else
1790       {
1791         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1793         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1794           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1795       }
1796       if (buffer[end_offset] != '\0')
1797       {
1798         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1799           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1800         if (buffer[end_offset +1] != '\0')
1801         {
1802           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1803             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1804         }
1805       }
1806     }
1807     DestroyWindow(hwndRichEdit);
1808     hwndRichEdit = NULL;
1809   }
1810 
1811   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1812   /* Test just the first two URL examples for brevity */
1813   for (i = 0; i < 2; i++) {
1814     SETTEXTEX st;
1815 
1816     hwndRichEdit = new_richedit(parent);
1817 
1818     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1819        be detected:
1820        1) Set entire text, a la WM_SETTEXT
1821        2) Set a selection of the text to the URL
1822        3) Set a portion of the text at a time, which eventually results in
1823           an URL
1824        All of them should give equivalent results
1825      */
1826 
1827     /* Set entire text in one go, like WM_SETTEXT */
1828     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1829       char * at_pos;
1830       int at_offset;
1831       int end_offset;
1832 
1833       st.codepage = CP_ACP;
1834       st.flags = ST_DEFAULT;
1835 
1836       at_pos = strchr(templates_delim[j], 'X');
1837       at_offset = at_pos - templates_delim[j];
1838       strncpy(buffer, templates_delim[j], at_offset);
1839       buffer[at_offset] = '\0';
1840       strcat(buffer, urls[i].text);
1841       strcat(buffer, templates_delim[j] + at_offset + 1);
1842       end_offset = at_offset + strlen(urls[i].text);
1843 
1844       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1845       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1846 
1847       /* This assumes no templates start with the URL itself, and that they
1848          have at least two characters before the URL text */
1849       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1850         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1851       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1852         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1853       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1854         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1855 
1856       if (urls[i].is_url)
1857       {
1858         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1859           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1860         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1861           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1862       }
1863       else
1864       {
1865         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1867         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1869       }
1870       if (buffer[end_offset] != '\0')
1871       {
1872         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1873           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1874         if (buffer[end_offset +1] != '\0')
1875         {
1876           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1877             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1878         }
1879       }
1880     }
1881 
1882     /* Set selection with X to the URL */
1883     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1884       char * at_pos;
1885       int at_offset;
1886       int end_offset;
1887 
1888       at_pos = strchr(templates_delim[j], 'X');
1889       at_offset = at_pos - templates_delim[j];
1890       end_offset = at_offset + strlen(urls[i].text);
1891 
1892       st.codepage = CP_ACP;
1893       st.flags = ST_DEFAULT;
1894       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1895       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1896       st.flags = ST_SELECTION;
1897       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1898       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1899       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1900 
1901       /* This assumes no templates start with the URL itself, and that they
1902          have at least two characters before the URL text */
1903       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1904         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1905       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1906         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1907       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1908         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1909 
1910       if (urls[i].is_url)
1911       {
1912         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1914         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1916       }
1917       else
1918       {
1919         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1920           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1921         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1922           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1923       }
1924       if (buffer[end_offset] != '\0')
1925       {
1926         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1927           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1928         if (buffer[end_offset +1] != '\0')
1929         {
1930           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1931             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1932         }
1933       }
1934     }
1935 
1936     /* Set selection with X to the first character of the URL, then the rest */
1937     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1938       char * at_pos;
1939       int at_offset;
1940       int end_offset;
1941 
1942       at_pos = strchr(templates_delim[j], 'X');
1943       at_offset = at_pos - templates_delim[j];
1944       end_offset = at_offset + strlen(urls[i].text);
1945 
1946       strcpy(buffer, "YY");
1947       buffer[0] = urls[i].text[0];
1948 
1949       st.codepage = CP_ACP;
1950       st.flags = ST_DEFAULT;
1951       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1952       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1953       st.flags = ST_SELECTION;
1954       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1955       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1956       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1957       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1958       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1959 
1960       /* This assumes no templates start with the URL itself, and that they
1961          have at least two characters before the URL text */
1962       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1963         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1964       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1965         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1966       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1967         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1968 
1969       if (urls[i].is_url)
1970       {
1971         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1972           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1973         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1974           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1975       }
1976       else
1977       {
1978         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1980         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1981           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1982       }
1983       if (buffer[end_offset] != '\0')
1984       {
1985         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1986           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1987         if (buffer[end_offset +1] != '\0')
1988         {
1989           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1990             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1991         }
1992       }
1993     }
1994 
1995     DestroyWindow(hwndRichEdit);
1996     hwndRichEdit = NULL;
1997   }
1998 
1999   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2000   /* Test just the first two URL examples for brevity */
2001   for (i = 0; i < 2; i++) {
2002     hwndRichEdit = new_richedit(parent);
2003 
2004     /* Set selection with X to the URL */
2005     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2006       char * at_pos;
2007       int at_offset;
2008       int end_offset;
2009 
2010       at_pos = strchr(templates_delim[j], 'X');
2011       at_offset = at_pos - templates_delim[j];
2012       end_offset = at_offset + strlen(urls[i].text);
2013 
2014       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2015       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2016       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2017       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2018       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2019 
2020       /* This assumes no templates start with the URL itself, and that they
2021          have at least two characters before the URL text */
2022       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2023         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2024       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2025         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2026       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2027         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2028 
2029       if (urls[i].is_url)
2030       {
2031         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2032           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2033         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2034           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2035       }
2036       else
2037       {
2038         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2039           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2040         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2041           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2042       }
2043       if (buffer[end_offset] != '\0')
2044       {
2045         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2046           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2047         if (buffer[end_offset +1] != '\0')
2048         {
2049           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2050             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2051         }
2052       }
2053     }
2054 
2055     /* Set selection with X to the first character of the URL, then the rest */
2056     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2057       char * at_pos;
2058       int at_offset;
2059       int end_offset;
2060 
2061       at_pos = strchr(templates_delim[j], 'X');
2062       at_offset = at_pos - templates_delim[j];
2063       end_offset = at_offset + strlen(urls[i].text);
2064 
2065       strcpy(buffer, "YY");
2066       buffer[0] = urls[i].text[0];
2067 
2068       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2069       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2070       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2071       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2072       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2073       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2074       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2075 
2076       /* This assumes no templates start with the URL itself, and that they
2077          have at least two characters before the URL text */
2078       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2079         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2080       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2081         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2082       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2083         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2084 
2085       if (urls[i].is_url)
2086       {
2087         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2088           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2089         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2090           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2091       }
2092       else
2093       {
2094         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2095           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2096         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2097           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2098       }
2099       if (buffer[end_offset] != '\0')
2100       {
2101         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2102           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2103         if (buffer[end_offset +1] != '\0')
2104         {
2105           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2106             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2107         }
2108       }
2109     }
2110 
2111     DestroyWindow(hwndRichEdit);
2112     hwndRichEdit = NULL;
2113   }
2114 
2115   DestroyWindow(parent);
2116 }
2117 
2118 static void test_EM_SCROLL(void)
2119 {
2120   int i, j;
2121   int r; /* return value */
2122   int expr; /* expected return value */
2123   HWND hwndRichEdit = new_richedit(NULL);
2124   int y_before, y_after; /* units of lines of text */
2125 
2126   /* test a richedit box containing a single line of text */
2127   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2128   expr = 0x00010000;
2129   for (i = 0; i < 4; i++) {
2130     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2131 
2132     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2133     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2134     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2135        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2136     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2137        "(i == %d)\n", y_after, i);
2138   }
2139 
2140   /*
2141    * test a richedit box that will scroll. There are two general
2142    * cases: the case without any long lines and the case with a long
2143    * line.
2144    */
2145   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2146     if (i == 0)
2147       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2148     else
2149       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2150                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2151                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2152                   "LONG LINE \nb\nc\nd\ne");
2153     for (j = 0; j < 12; j++) /* reset scroll position to top */
2154       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2155 
2156     /* get first visible line */
2157     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2158     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2159 
2160     /* get new current first visible line */
2161     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2162 
2163     ok(((r & 0xffffff00) == 0x00010000) &&
2164        ((r & 0x000000ff) != 0x00000000),
2165        "EM_SCROLL page down didn't scroll by a small positive number of "
2166        "lines (r == 0x%08x)\n", r);
2167     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2168        "(line %d scrolled to line %d\n", y_before, y_after);
2169 
2170     y_before = y_after;
2171     
2172     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2173     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2174     ok(((r & 0xffffff00) == 0x0001ff00),
2175        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2176        "(r == 0x%08x)\n", r);
2177     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2178        "%d scrolled to line %d\n", y_before, y_after);
2179     
2180     y_before = y_after;
2181 
2182     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2183 
2184     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2185 
2186     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2187        "(r == 0x%08x)\n", r);
2188     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2189        "1 line (%d scrolled to %d)\n", y_before, y_after);
2190 
2191     y_before = y_after;
2192 
2193     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2194 
2195     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2196 
2197     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2198        "(r == 0x%08x)\n", r);
2199     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2200        "line (%d scrolled to %d)\n", y_before, y_after);
2201 
2202     y_before = y_after;
2203 
2204     r = SendMessage(hwndRichEdit, EM_SCROLL,
2205                     SB_LINEUP, 0); /* lineup beyond top */
2206 
2207     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2208 
2209     ok(r == 0x00010000,
2210        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2211     ok(y_before == y_after,
2212        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2213 
2214     y_before = y_after;
2215 
2216     r = SendMessage(hwndRichEdit, EM_SCROLL,
2217                     SB_PAGEUP, 0);/*page up beyond top */
2218 
2219     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2220 
2221     ok(r == 0x00010000,
2222        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2223     ok(y_before == y_after,
2224        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2225 
2226     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2227       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2228     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2229     r = SendMessage(hwndRichEdit, EM_SCROLL,
2230                     SB_PAGEDOWN, 0); /* page down beyond bot */
2231     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2232 
2233     ok(r == 0x00010000,
2234        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2235     ok(y_before == y_after,
2236        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2237        y_before, y_after);
2238 
2239     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2240     SendMessage(hwndRichEdit, EM_SCROLL,
2241                 SB_LINEDOWN, 0); /* line down beyond bot */
2242     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2243     
2244     ok(r == 0x00010000,
2245        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2246     ok(y_before == y_after,
2247        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2248        y_before, y_after);
2249   }
2250   DestroyWindow(hwndRichEdit);
2251 }
2252 
2253 unsigned int recursionLevel = 0;
2254 unsigned int WM_SIZE_recursionLevel = 0;
2255 BOOL bailedOutOfRecursion = FALSE;
2256 LRESULT WINAPI (*richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2257 
2258 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2259 {
2260     LRESULT r;
2261 
2262     if (bailedOutOfRecursion) return 0;
2263     if (recursionLevel >= 32) {
2264         bailedOutOfRecursion = TRUE;
2265         return 0;
2266     }
2267 
2268     recursionLevel++;
2269     switch (message) {
2270     case WM_SIZE:
2271         WM_SIZE_recursionLevel++;
2272         r = richeditProc(hwnd, message, wParam, lParam);
2273         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2274         ShowScrollBar(hwnd, SB_VERT, TRUE);
2275         WM_SIZE_recursionLevel--;
2276         break;
2277     default:
2278         r = richeditProc(hwnd, message, wParam, lParam);
2279         break;
2280     }
2281     recursionLevel--;
2282     return r;
2283 }
2284 
2285 static void test_scrollbar_visibility(void)
2286 {
2287   HWND hwndRichEdit;
2288   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2289   SCROLLINFO si;
2290   WNDCLASSA cls;
2291   BOOL r;
2292 
2293   /* These tests show that richedit should temporarily refrain from automatically
2294      hiding or showing its scrollbars (vertical at least) when an explicit request
2295      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2296      Some applications depend on forced showing (when otherwise richedit would
2297      hide the vertical scrollbar) and are thrown on an endless recursive loop
2298      if richedit auto-hides the scrollbar again. Apparently they never heard of
2299      the ES_DISABLENOSCROLL style... */
2300 
2301   hwndRichEdit = new_richedit(NULL);
2302 
2303   /* Test default scrollbar visibility behavior */
2304   memset(&si, 0, sizeof(si));
2305   si.cbSize = sizeof(si);
2306   si.fMask = SIF_PAGE | SIF_RANGE;
2307   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2308   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2309     "Vertical scrollbar is visible, should be invisible.\n");
2310   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2311         "reported page/range is %d (%d..%d) expected all 0\n",
2312         si.nPage, si.nMin, si.nMax);
2313 
2314   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2315   memset(&si, 0, sizeof(si));
2316   si.cbSize = sizeof(si);
2317   si.fMask = SIF_PAGE | SIF_RANGE;
2318   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2319   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2320     "Vertical scrollbar is visible, should be invisible.\n");
2321   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2322         "reported page/range is %d (%d..%d) expected all 0\n",
2323         si.nPage, si.nMin, si.nMax);
2324 
2325   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2326   memset(&si, 0, sizeof(si));
2327   si.cbSize = sizeof(si);
2328   si.fMask = SIF_PAGE | SIF_RANGE;
2329   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2330   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2331     "Vertical scrollbar is invisible, should be visible.\n");
2332   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2333         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2334         si.nPage, si.nMin, si.nMax);
2335 
2336   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2337      even though it hides the scrollbar */
2338   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2339   memset(&si, 0, sizeof(si));
2340   si.cbSize = sizeof(si);
2341   si.fMask = SIF_PAGE | SIF_RANGE;
2342   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2343   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2344     "Vertical scrollbar is visible, should be invisible.\n");
2345   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2346         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2347         si.nPage, si.nMin, si.nMax);
2348 
2349   /* Setting non-scrolling text again does *not* reset scrollbar range */
2350   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2351   memset(&si, 0, sizeof(si));
2352   si.cbSize = sizeof(si);
2353   si.fMask = SIF_PAGE | SIF_RANGE;
2354   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2355   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2356     "Vertical scrollbar is visible, should be invisible.\n");
2357   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2358         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2359         si.nPage, si.nMin, si.nMax);
2360 
2361   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2362   memset(&si, 0, sizeof(si));
2363   si.cbSize = sizeof(si);
2364   si.fMask = SIF_PAGE | SIF_RANGE;
2365   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2366   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2367     "Vertical scrollbar is visible, should be invisible.\n");
2368   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2369         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2370         si.nPage, si.nMin, si.nMax);
2371 
2372   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2373   memset(&si, 0, sizeof(si));
2374   si.cbSize = sizeof(si);
2375   si.fMask = SIF_PAGE | SIF_RANGE;
2376   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2377   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2378     "Vertical scrollbar is visible, should be invisible.\n");
2379   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2380         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2381         si.nPage, si.nMin, si.nMax);
2382 
2383   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2384   memset(&si, 0, sizeof(si));
2385   si.cbSize = sizeof(si);
2386   si.fMask = SIF_PAGE | SIF_RANGE;
2387   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2388   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2389     "Vertical scrollbar is visible, should be invisible.\n");
2390   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2391         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2392         si.nPage, si.nMin, si.nMax);
2393 
2394   DestroyWindow(hwndRichEdit);
2395 
2396   /* Test again, with ES_DISABLENOSCROLL style */
2397   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2398 
2399   /* Test default scrollbar visibility behavior */
2400   memset(&si, 0, sizeof(si));
2401   si.cbSize = sizeof(si);
2402   si.fMask = SIF_PAGE | SIF_RANGE;
2403   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2404   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2405     "Vertical scrollbar is invisible, should be visible.\n");
2406   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2407         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2408         si.nPage, si.nMin, si.nMax);
2409 
2410   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2411   memset(&si, 0, sizeof(si));
2412   si.cbSize = sizeof(si);
2413   si.fMask = SIF_PAGE | SIF_RANGE;
2414   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2415   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2416     "Vertical scrollbar is invisible, should be visible.\n");
2417   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2418         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2419         si.nPage, si.nMin, si.nMax);
2420 
2421   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2422   memset(&si, 0, sizeof(si));
2423   si.cbSize = sizeof(si);
2424   si.fMask = SIF_PAGE | SIF_RANGE;
2425   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2426   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2427     "Vertical scrollbar is invisible, should be visible.\n");
2428   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2429         "reported page/range is %d (%d..%d)\n",
2430         si.nPage, si.nMin, si.nMax);
2431 
2432   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2433   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2434   memset(&si, 0, sizeof(si));
2435   si.cbSize = sizeof(si);
2436   si.fMask = SIF_PAGE | SIF_RANGE;
2437   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2438   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2439     "Vertical scrollbar is invisible, should be visible.\n");
2440   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2441         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2442         si.nPage, si.nMin, si.nMax);
2443 
2444   /* Setting non-scrolling text again does *not* reset scrollbar range */
2445   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2446   memset(&si, 0, sizeof(si));
2447   si.cbSize = sizeof(si);
2448   si.fMask = SIF_PAGE | SIF_RANGE;
2449   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2450   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2451     "Vertical scrollbar is invisible, should be visible.\n");
2452   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2453         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2454         si.nPage, si.nMin, si.nMax);
2455 
2456   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2457   memset(&si, 0, sizeof(si));
2458   si.cbSize = sizeof(si);
2459   si.fMask = SIF_PAGE | SIF_RANGE;
2460   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2461   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2462     "Vertical scrollbar is invisible, should be visible.\n");
2463   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2464         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2465         si.nPage, si.nMin, si.nMax);
2466 
2467   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2468   memset(&si, 0, sizeof(si));
2469   si.cbSize = sizeof(si);
2470   si.fMask = SIF_PAGE | SIF_RANGE;
2471   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2472   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2473     "Vertical scrollbar is invisible, should be visible.\n");
2474   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2475         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2476         si.nPage, si.nMin, si.nMax);
2477 
2478   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2479   memset(&si, 0, sizeof(si));
2480   si.cbSize = sizeof(si);
2481   si.fMask = SIF_PAGE | SIF_RANGE;
2482   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2483   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2484     "Vertical scrollbar is invisible, should be visible.\n");
2485   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2486         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2487         si.nPage, si.nMin, si.nMax);
2488 
2489   DestroyWindow(hwndRichEdit);
2490 
2491   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2492   hwndRichEdit = new_richedit(NULL);
2493 
2494   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2495   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2496   memset(&si, 0, sizeof(si));
2497   si.cbSize = sizeof(si);
2498   si.fMask = SIF_PAGE | SIF_RANGE;
2499   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2500   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2501     "Vertical scrollbar is invisible, should be visible.\n");
2502   todo_wine {
2503   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2504         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2505         si.nPage, si.nMin, si.nMax);
2506   }
2507 
2508   /* Ditto, see above */
2509   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2510   memset(&si, 0, sizeof(si));
2511   si.cbSize = sizeof(si);
2512   si.fMask = SIF_PAGE | SIF_RANGE;
2513   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2514   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2515     "Vertical scrollbar is invisible, should be visible.\n");
2516   todo_wine {
2517   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2518         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2519         si.nPage, si.nMin, si.nMax);
2520   }
2521 
2522   /* Ditto, see above */
2523   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2524   memset(&si, 0, sizeof(si));
2525   si.cbSize = sizeof(si);
2526   si.fMask = SIF_PAGE | SIF_RANGE;
2527   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2528   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2529     "Vertical scrollbar is invisible, should be visible.\n");
2530   todo_wine {
2531   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2532         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2533         si.nPage, si.nMin, si.nMax);
2534   }
2535 
2536   /* Ditto, see above */
2537   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2538   memset(&si, 0, sizeof(si));
2539   si.cbSize = sizeof(si);
2540   si.fMask = SIF_PAGE | SIF_RANGE;
2541   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2542   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2543     "Vertical scrollbar is invisible, should be visible.\n");
2544   todo_wine {
2545   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2546         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2547         si.nPage, si.nMin, si.nMax);
2548   }
2549 
2550   /* Ditto, see above */
2551   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552   memset(&si, 0, sizeof(si));
2553   si.cbSize = sizeof(si);
2554   si.fMask = SIF_PAGE | SIF_RANGE;
2555   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2557     "Vertical scrollbar is invisible, should be visible.\n");
2558   todo_wine {
2559   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2560         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2561         si.nPage, si.nMin, si.nMax);
2562   }
2563 
2564   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2565   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2566   memset(&si, 0, sizeof(si));
2567   si.cbSize = sizeof(si);
2568   si.fMask = SIF_PAGE | SIF_RANGE;
2569   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571     "Vertical scrollbar is visible, should be invisible.\n");
2572   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2573         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2574         si.nPage, si.nMin, si.nMax);
2575 
2576   DestroyWindow(hwndRichEdit);
2577 
2578   hwndRichEdit = new_richedit(NULL);
2579 
2580   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2581   memset(&si, 0, sizeof(si));
2582   si.cbSize = sizeof(si);
2583   si.fMask = SIF_PAGE | SIF_RANGE;
2584   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2585   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2586     "Vertical scrollbar is visible, should be invisible.\n");
2587   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2588         "reported page/range is %d (%d..%d) expected all 0\n",
2589         si.nPage, si.nMin, si.nMax);
2590 
2591   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2592   memset(&si, 0, sizeof(si));
2593   si.cbSize = sizeof(si);
2594   si.fMask = SIF_PAGE | SIF_RANGE;
2595   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2596   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2597     "Vertical scrollbar is visible, should be invisible.\n");
2598   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2599         "reported page/range is %d (%d..%d) expected all 0\n",
2600         si.nPage, si.nMin, si.nMax);
2601 
2602   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2603   memset(&si, 0, sizeof(si));
2604   si.cbSize = sizeof(si);
2605   si.fMask = SIF_PAGE | SIF_RANGE;
2606   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2607   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2608     "Vertical scrollbar is visible, should be invisible.\n");
2609   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2610         "reported page/range is %d (%d..%d) expected all 0\n",
2611         si.nPage, si.nMin, si.nMax);
2612 
2613   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2614   memset(&si, 0, sizeof(si));
2615   si.cbSize = sizeof(si);
2616   si.fMask = SIF_PAGE | SIF_RANGE;
2617   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2618   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2619     "Vertical scrollbar is visible, should be invisible.\n");
2620   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2621         "reported page/range is %d (%d..%d) expected all 0\n",
2622         si.nPage, si.nMin, si.nMax);
2623 
2624   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2625   memset(&si, 0, sizeof(si));
2626   si.cbSize = sizeof(si);
2627   si.fMask = SIF_PAGE | SIF_RANGE;
2628   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2629   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2630     "Vertical scrollbar is invisible, should be visible.\n");
2631   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2632         "reported page/range is %d (%d..%d)\n",
2633         si.nPage, si.nMin, si.nMax);
2634 
2635   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2636   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2637   memset(&si, 0, sizeof(si));
2638   si.cbSize = sizeof(si);
2639   si.fMask = SIF_PAGE | SIF_RANGE;
2640   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2641   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2642     "Vertical scrollbar is visible, should be invisible.\n");
2643   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2644         "reported page/range is %d (%d..%d)\n",
2645         si.nPage, si.nMin, si.nMax);
2646 
2647   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2648   memset(&si, 0, sizeof(si));
2649   si.cbSize = sizeof(si);
2650   si.fMask = SIF_PAGE | SIF_RANGE;
2651   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2653     "Vertical scrollbar is visible, should be invisible.\n");
2654   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2655         "reported page/range is %d (%d..%d)\n",
2656         si.nPage, si.nMin, si.nMax);
2657 
2658   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2659      EM_SCROLL will make visible any forcefully invisible scrollbar */
2660   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2661   memset(&si, 0, sizeof(si));
2662   si.cbSize = sizeof(si);
2663   si.fMask = SIF_PAGE | SIF_RANGE;
2664   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2665   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2666     "Vertical scrollbar is invisible, should be visible.\n");
2667   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2668         "reported page/range is %d (%d..%d)\n",
2669         si.nPage, si.nMin, si.nMax);
2670 
2671   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2672   memset(&si, 0, sizeof(si));
2673   si.cbSize = sizeof(si);
2674   si.fMask = SIF_PAGE | SIF_RANGE;
2675   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2676   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2677     "Vertical scrollbar is visible, should be invisible.\n");
2678   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2679         "reported page/range is %d (%d..%d)\n",
2680         si.nPage, si.nMin, si.nMax);
2681 
2682   /* Again, EM_SCROLL, with SB_LINEUP */
2683   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2684   memset(&si, 0, sizeof(si));
2685   si.cbSize = sizeof(si);
2686   si.fMask = SIF_PAGE | SIF_RANGE;
2687   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2689     "Vertical scrollbar is invisible, should be visible.\n");
2690   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2691         "reported page/range is %d (%d..%d)\n",
2692         si.nPage, si.nMin, si.nMax);
2693 
2694   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2695   memset(&si, 0, sizeof(si));
2696   si.cbSize = sizeof(si);
2697   si.fMask = SIF_PAGE | SIF_RANGE;
2698   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2699   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2700     "Vertical scrollbar is visible, should be invisible.\n");
2701   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2702         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2703         si.nPage, si.nMin, si.nMax);
2704 
2705   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2706   memset(&si, 0, sizeof(si));
2707   si.cbSize = sizeof(si);
2708   si.fMask = SIF_PAGE | SIF_RANGE;
2709   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2710   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2711     "Vertical scrollbar is invisible, should be visible.\n");
2712   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2713         "reported page/range is %d (%d..%d)\n",
2714         si.nPage, si.nMin, si.nMax);
2715 
2716   DestroyWindow(hwndRichEdit);
2717 
2718 
2719   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2720   hwndRichEdit = new_richedit(NULL);
2721 
2722 #define ENABLE_WS_VSCROLL(hwnd) \
2723     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2724 #define DISABLE_WS_VSCROLL(hwnd) \
2725     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2726 
2727   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2728   ENABLE_WS_VSCROLL(hwndRichEdit);
2729   memset(&si, 0, sizeof(si));
2730   si.cbSize = sizeof(si);
2731   si.fMask = SIF_PAGE | SIF_RANGE;
2732   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2733   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2734     "Vertical scrollbar is invisible, should be visible.\n");
2735   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2736         "reported page/range is %d (%d..%d) expected all 0\n",
2737         si.nPage, si.nMin, si.nMax);
2738 
2739   /* Ditto, see above */
2740   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2741   memset(&si, 0, sizeof(si));
2742   si.cbSize = sizeof(si);
2743   si.fMask = SIF_PAGE | SIF_RANGE;
2744   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746     "Vertical scrollbar is invisible, should be visible.\n");
2747   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2748         "reported page/range is %d (%d..%d) expected all 0\n",
2749         si.nPage, si.nMin, si.nMax);
2750 
2751   /* Ditto, see above */
2752   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2753   memset(&si, 0, sizeof(si));
2754   si.cbSize = sizeof(si);
2755   si.fMask = SIF_PAGE | SIF_RANGE;
2756   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2758     "Vertical scrollbar is invisible, should be visible.\n");
2759   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2760         "reported page/range is %d (%d..%d) expected all 0\n",
2761         si.nPage, si.nMin, si.nMax);
2762 
2763   /* Ditto, see above */
2764   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2765   memset(&si, 0, sizeof(si));
2766   si.cbSize = sizeof(si);
2767   si.fMask = SIF_PAGE | SIF_RANGE;
2768   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2769   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2770     "Vertical scrollbar is invisible, should be visible.\n");
2771   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2772         "reported page/range is %d (%d..%d) expected all 0\n",
2773         si.nPage, si.nMin, si.nMax);
2774 
2775   /* Ditto, see above */
2776   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2777   memset(&si, 0, sizeof(si));
2778   si.cbSize = sizeof(si);
2779   si.fMask = SIF_PAGE | SIF_RANGE;
2780   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2781   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2782     "Vertical scrollbar is invisible, should be visible.\n");
2783   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2784         "reported page/range is %d (%d..%d) expected all 0\n",
2785         si.nPage, si.nMin, si.nMax);
2786 
2787   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2788   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2789   memset(&si, 0, sizeof(si));
2790   si.cbSize = sizeof(si);
2791   si.fMask = SIF_PAGE | SIF_RANGE;
2792   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2793   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2794     "Vertical scrollbar is visible, should be invisible.\n");
2795   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2796         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2797         si.nPage, si.nMin, si.nMax);
2798 
2799   DestroyWindow(hwndRichEdit);
2800 
2801   hwndRichEdit = new_richedit(NULL);
2802 
2803   DISABLE_WS_VSCROLL(hwndRichEdit);
2804   memset(&si, 0, sizeof(si));
2805   si.cbSize = sizeof(si);
2806   si.fMask = SIF_PAGE | SIF_RANGE;
2807   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809     "Vertical scrollbar is visible, should be invisible.\n");
2810   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2811         "reported page/range is %d (%d..%d) expected all 0\n",
2812         si.nPage, si.nMin, si.nMax);
2813 
2814   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2815   memset(&si, 0, sizeof(si));
2816   si.cbSize = sizeof(si);
2817   si.fMask = SIF_PAGE | SIF_RANGE;
2818   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2820     "Vertical scrollbar is visible, should be invisible.\n");
2821   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2822         "reported page/range is %d (%d..%d) expected all 0\n",
2823         si.nPage, si.nMin, si.nMax);
2824 
2825   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2826   memset(&si, 0, sizeof(si));
2827   si.cbSize = sizeof(si);
2828   si.fMask = SIF_PAGE | SIF_RANGE;
2829   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2830   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2831     "Vertical scrollbar is visible, should be invisible.\n");
2832   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2833         "reported page/range is %d (%d..%d) expected all 0\n",
2834         si.nPage, si.nMin, si.nMax);
2835 
2836   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2837   memset(&si, 0, sizeof(si));
2838   si.cbSize = sizeof(si);
2839   si.fMask = SIF_PAGE | SIF_RANGE;
2840   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2841   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2842     "Vertical scrollbar is visible, should be invisible.\n");
2843   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2844         "reported page/range is %d (%d..%d) expected all 0\n",
2845         si.nPage, si.nMin, si.nMax);
2846 
2847   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2848   memset(&si, 0, sizeof(si));
2849   si.cbSize = sizeof(si);
2850   si.fMask = SIF_PAGE | SIF_RANGE;
2851   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2852   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2853     "Vertical scrollbar is invisible, should be visible.\n");
2854   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2855         "reported page/range is %d (%d..%d)\n",
2856         si.nPage, si.nMin, si.nMax);
2857 
2858   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2859   DISABLE_WS_VSCROLL(hwndRichEdit);
2860   memset(&si, 0, sizeof(si));
2861   si.cbSize = sizeof(si);
2862   si.fMask = SIF_PAGE | SIF_RANGE;
2863   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2864   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2865     "Vertical scrollbar is visible, should be invisible.\n");
2866   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2867         "reported page/range is %d (%d..%d)\n",
2868         si.nPage, si.nMin, si.nMax);
2869 
2870   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2871   memset(&si, 0, sizeof(si));
2872   si.cbSize = sizeof(si);
2873   si.fMask = SIF_PAGE | SIF_RANGE;
2874   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2875   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2876     "Vertical scrollbar is visible, should be invisible.\n");
2877   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2878         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2879         si.nPage, si.nMin, si.nMax);
2880 
2881   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2882   memset(&si, 0, sizeof(si));
2883   si.cbSize = sizeof(si);
2884   si.fMask = SIF_PAGE | SIF_RANGE;
2885   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2886   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2887     "Vertical scrollbar is invisible, should be visible.\n");
2888   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2889         "reported page/range is %d (%d..%d)\n",
2890         si.nPage, si.nMin, si.nMax);
2891 
2892   DISABLE_WS_VSCROLL(hwndRichEdit);
2893   memset(&si, 0, sizeof(si));
2894   si.cbSize = sizeof(si);
2895   si.fMask = SIF_PAGE | SIF_RANGE;
2896   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2897   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2898     "Vertical scrollbar is visible, should be invisible.\n");
2899   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2900         "reported page/range is %d (%d..%d)\n",
2901         si.nPage, si.nMin, si.nMax);
2902 
2903   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2904      EM_SCROLL will make visible any forcefully invisible scrollbar */
2905   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2906   memset(&si, 0, sizeof(si));
2907   si.cbSize = sizeof(si);
2908   si.fMask = SIF_PAGE | SIF_RANGE;
2909   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2910   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2911     "Vertical scrollbar is invisible, should be visible.\n");
2912   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2913         "reported page/range is %d (%d..%d)\n",
2914         si.nPage, si.nMin, si.nMax);
2915 
2916   DISABLE_WS_VSCROLL(hwndRichEdit);
2917   memset(&si, 0, sizeof(si));
2918   si.cbSize = sizeof(si);
2919   si.fMask = SIF_PAGE | SIF_RANGE;
2920   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2921   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2922     "Vertical scrollbar is visible, should be invisible.\n");
2923   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2924         "reported page/range is %d (%d..%d)\n",
2925         si.nPage, si.nMin, si.nMax);
2926 
2927   /* Again, EM_SCROLL, with SB_LINEUP */
2928   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2929   memset(&si, 0, sizeof(si));
2930   si.cbSize = sizeof(si);
2931   si.fMask = SIF_PAGE | SIF_RANGE;
2932   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2933   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2934     "Vertical scrollbar is invisible, should be visible.\n");
2935   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2936         "reported page/range is %d (%d..%d)\n",
2937         si.nPage, si.nMin, si.nMax);
2938 
2939   DestroyWindow(hwndRichEdit);
2940 
2941   /* This window proc models what is going on with Corman Lisp 3.0.
2942      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2943      force the scrollbar into visibility. Recursion should NOT happen
2944      as a result of this action.
2945    */
2946   r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2947   if (r) {
2948     richeditProc = cls.lpfnWndProc;
2949     cls.lpfnWndProc = RicheditStupidOverrideProcA;
2950     cls.lpszClassName = "RicheditStupidOverride";
2951     if(!RegisterClassA(&cls)) assert(0);
2952 
2953     recursionLevel = 0;
2954     WM_SIZE_recursionLevel = 0;
2955     bailedOutOfRecursion = FALSE;
2956     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2957     ok(!bailedOutOfRecursion,
2958         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2959 
2960     recursionLevel = 0;
2961     WM_SIZE_recursionLevel = 0;
2962     bailedOutOfRecursion = FALSE;
2963     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2964     ok(!bailedOutOfRecursion,
2965         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2966 
2967     /* Unblock window in order to process WM_DESTROY */
2968     recursionLevel = 0;
2969     bailedOutOfRecursion = FALSE;
2970     WM_SIZE_recursionLevel = 0;
2971     DestroyWindow(hwndRichEdit);
2972   }
2973 }
2974 
2975 static void test_EM_SETUNDOLIMIT(void)
2976 {
2977   /* cases we test for:
2978    * default behaviour - limiting at 100 undo's 
2979    * undo disabled - setting a limit of 0
2980    * undo limited -  undo limit set to some to some number, like 2
2981    * bad input - sending a negative number should default to 100 undo's */
2982  
2983   HWND hwndRichEdit = new_richedit(NULL);
2984   CHARRANGE cr;
2985   int i;
2986   int result;
2987   
2988   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2989   cr.cpMin = 0;
2990   cr.cpMax = 1;
2991   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2992     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2993       also, multiple pastes don't combine like WM_CHAR would */
2994   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2995 
2996   /* first case - check the default */
2997   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
2998   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2999     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3000   for (i=0; i<100; i++) /* Undo 100 of them */
3001     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3002   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3003      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3004 
3005   /* second case - cannot undo */
3006   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3007   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
3008   SendMessage(hwndRichEdit,
3009               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3010   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3011      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3012 
3013   /* third case - set it to an arbitrary number */
3014   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3015