~ [ 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   todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
618   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
619 
620   /* A valid flag, CHARFORMAT2 structure minimally filled */
621   memset(&cf2, 0, sizeof(cf2));
622   cf2.cbSize = sizeof(CHARFORMAT2);
623   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
624              (LPARAM) &cf2);
625   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
626   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
627   ok(rc == FALSE, "Should not be able to undo here.\n");
628   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
629 
630   /* A valid flag, CHARFORMAT2 structure minimally filled */
631   memset(&cf2, 0, sizeof(cf2));
632   cf2.cbSize = sizeof(CHARFORMAT2);
633   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
634              (LPARAM) &cf2);
635   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
636   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
637   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
638   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
639 
640   /* A valid flag, CHARFORMAT2 structure minimally filled */
641   memset(&cf2, 0, sizeof(cf2));
642   cf2.cbSize = sizeof(CHARFORMAT2);
643   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
644              (LPARAM) &cf2);
645   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
646   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
647   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
648   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
649 
650   cf2.cbSize = sizeof(CHARFORMAT2);
651   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
652              (LPARAM) &cf2);
653 
654   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
655   cf2.cbSize = sizeof(CHARFORMAT2);
656   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
657              (LPARAM) &cf2);
658   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
659   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
660 
661   /* wParam==0 is default char format, does not set modify */
662   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
663   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
664   ok(rc == 0, "Text marked as modified, expected not modified!\n");
665   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
666   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
667   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
668   ok(rc == 0, "Text marked as modified, expected not modified!\n");
669 
670   /* wParam==SCF_SELECTION sets modify if nonempty selection */
671   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673   ok(rc == 0, "Text marked as modified, expected not modified!\n");
674   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
675   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677   ok(rc == 0, "Text marked as modified, expected not modified!\n");
678 
679   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
680   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
681   ok(rc == 0, "Text marked as modified, expected not modified!\n");
682   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
683   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
684   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
685   ok(rc == 0, "Text marked as modified, expected not modified!\n");
686   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
687   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
688   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
689   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
691 
692   /* wParam==SCF_ALL sets modify regardless of whether text is present */
693   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
694   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
695   ok(rc == 0, "Text marked as modified, expected not modified!\n");
696   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
697   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
700 
701   DestroyWindow(hwndRichEdit);
702 
703   /* EM_GETCHARFORMAT tests */
704   for (i = 0; tested_effects[i]; i++)
705   {
706     hwndRichEdit = new_richedit(NULL);
707     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
708 
709     /* Need to set a TrueType font to get consistent CFM_BOLD results */
710     memset(&cf2, 0, sizeof(CHARFORMAT2));
711     cf2.cbSize = sizeof(CHARFORMAT2);
712     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
713     cf2.dwEffects = 0;
714     strcpy(cf2.szFaceName, "Courier New");
715     cf2.wWeight = FW_DONTCARE;
716     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
717 
718     memset(&cf2, 0, sizeof(CHARFORMAT2));
719     cf2.cbSize = sizeof(CHARFORMAT2);
720     SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
721     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
722     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
723           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
724           ||
725           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
726         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
727     ok((cf2.dwEffects & tested_effects[i]) == 0,
728         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
729 
730     memset(&cf2, 0, sizeof(CHARFORMAT2));
731     cf2.cbSize = sizeof(CHARFORMAT2);
732     cf2.dwMask = tested_effects[i];
733     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
734       cf2.dwMask = CFM_SUPERSCRIPT;
735     cf2.dwEffects = tested_effects[i];
736     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
737     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
738 
739     memset(&cf2, 0, sizeof(CHARFORMAT2));
740     cf2.cbSize = sizeof(CHARFORMAT2);
741     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
742     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
743     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
744           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
745           ||
746           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
747         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
748     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
749         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
750 
751     memset(&cf2, 0, sizeof(CHARFORMAT2));
752     cf2.cbSize = sizeof(CHARFORMAT2);
753     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
754     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
755     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
756           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
757           ||
758           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
759         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
760     ok((cf2.dwEffects & tested_effects[i]) == 0,
761         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
762 
763     memset(&cf2, 0, sizeof(CHARFORMAT2));
764     cf2.cbSize = sizeof(CHARFORMAT2);
765     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
766     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
767     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
768           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
769           ||
770           (cf2.dwMask & tested_effects[i]) == 0),
771         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
772 
773     DestroyWindow(hwndRichEdit);
774   }
775 
776   for (i = 0; tested_effects[i]; i++)
777   {
778     hwndRichEdit = new_richedit(NULL);
779     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
780 
781     /* Need to set a TrueType font to get consistent CFM_BOLD results */
782     memset(&cf2, 0, sizeof(CHARFORMAT2));
783     cf2.cbSize = sizeof(CHARFORMAT2);
784     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
785     cf2.dwEffects = 0;
786     strcpy(cf2.szFaceName, "Courier New");
787     cf2.wWeight = FW_DONTCARE;
788     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
789 
790     memset(&cf2, 0, sizeof(CHARFORMAT2));
791     cf2.cbSize = sizeof(CHARFORMAT2);
792     cf2.dwMask = tested_effects[i];
793     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
794       cf2.dwMask = CFM_SUPERSCRIPT;
795     cf2.dwEffects = tested_effects[i];
796     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
797     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
798 
799     memset(&cf2, 0, sizeof(CHARFORMAT2));
800     cf2.cbSize = sizeof(CHARFORMAT2);
801     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
802     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
803     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
804           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
805           ||
806           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
807         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
808     ok((cf2.dwEffects & tested_effects[i]) == 0,
809         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
810 
811     memset(&cf2, 0, sizeof(CHARFORMAT2));
812     cf2.cbSize = sizeof(CHARFORMAT2);
813     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
814     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
815     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
816           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
817           ||
818           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
819         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
820     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
821         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
822 
823     memset(&cf2, 0, sizeof(CHARFORMAT2));
824     cf2.cbSize = sizeof(CHARFORMAT2);
825     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
826     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
827     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
828           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
829           ||
830           (cf2.dwMask & tested_effects[i]) == 0),
831         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
832     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
833         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
834 
835     DestroyWindow(hwndRichEdit);
836   }
837 
838   /* Effects applied on an empty selection should take effect when selection is
839      replaced with text */
840   hwndRichEdit = new_richedit(NULL);
841   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
842   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
843 
844   memset(&cf2, 0, sizeof(CHARFORMAT2));
845   cf2.cbSize = sizeof(CHARFORMAT2);
846   cf2.dwMask = CFM_BOLD;
847   cf2.dwEffects = CFE_BOLD;
848   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
849 
850   /* Selection is now nonempty */
851   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
852 
853   memset(&cf2, 0, sizeof(CHARFORMAT2));
854   cf2.cbSize = sizeof(CHARFORMAT2);
855   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
856   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857 
858   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
859       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
860   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
861       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
862 
863 
864   /* Set two effects on an empty selection */
865   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
866   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
867 
868   memset(&cf2, 0, sizeof(CHARFORMAT2));
869   cf2.cbSize = sizeof(CHARFORMAT2);
870   cf2.dwMask = CFM_BOLD;
871   cf2.dwEffects = CFE_BOLD;
872   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
873   cf2.dwMask = CFM_ITALIC;
874   cf2.dwEffects = CFE_ITALIC;
875   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
876 
877   /* Selection is now nonempty */
878   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
879 
880   memset(&cf2, 0, sizeof(CHARFORMAT2));
881   cf2.cbSize = sizeof(CHARFORMAT2);
882   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
883   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
884 
885   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
886       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
887   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
888       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
889 
890   /* Setting the (empty) selection to exactly the same place as before should
891      NOT clear the insertion style! */
892   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
893   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
894 
895   memset(&cf2, 0, sizeof(CHARFORMAT2));
896   cf2.cbSize = sizeof(CHARFORMAT2);
897   cf2.dwMask = CFM_BOLD;
898   cf2.dwEffects = CFE_BOLD;
899   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
900 
901   /* Empty selection in same place, insert style should NOT be forgotten here. */
902   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
903 
904   /* Selection is now nonempty */
905   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
906 
907   memset(&cf2, 0, sizeof(CHARFORMAT2));
908   cf2.cbSize = sizeof(CHARFORMAT2);
909   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
910   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
911 
912   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
913       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
914   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
915       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
916 
917   /* Ditto with EM_EXSETSEL */
918   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
919   cr.cpMin = 2; cr.cpMax = 2;
920   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
921 
922   memset(&cf2, 0, sizeof(CHARFORMAT2));
923   cf2.cbSize = sizeof(CHARFORMAT2);
924   cf2.dwMask = CFM_BOLD;
925   cf2.dwEffects = CFE_BOLD;
926   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
927 
928   /* Empty selection in same place, insert style should NOT be forgotten here. */
929   cr.cpMin = 2; cr.cpMax = 2;
930   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
931 
932   /* Selection is now nonempty */
933   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
934 
935   memset(&cf2, 0, sizeof(CHARFORMAT2));
936   cf2.cbSize = sizeof(CHARFORMAT2);
937   cr.cpMin = 2; cr.cpMax = 6;
938   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
939   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
940 
941   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
942       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
943   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
944       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
945 
946   DestroyWindow(hwndRichEdit);
947 }
948 
949 static void test_EM_SETTEXTMODE(void)
950 {
951   HWND hwndRichEdit = new_richedit(NULL);
952   CHARFORMAT2 cf2, cf2test;
953   CHARRANGE cr;
954   int rc = 0;
955 
956   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
957   /*Insert text into the control*/
958 
959   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
960 
961   /*Attempt to change the control to plain text mode*/
962   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
963   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
964 
965   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
966   If rich text is pasted, it should have the same formatting as the rest
967   of the text in the control*/
968 
969   /*Italicize the text
970   *NOTE: If the default text was already italicized, the test will simply
971   reverse; in other words, it will copy a regular "wine" into a plain
972   text window that uses an italicized format*/
973   cf2.cbSize = sizeof(CHARFORMAT2);
974   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
975              (LPARAM) &cf2);
976 
977   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
978   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
979 
980   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
981   ok(rc == 0, "Text marked as modified, expected not modified!\n");
982 
983   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
984   however, SCF_ALL has been implemented*/
985   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
986   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
987 
988   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
989   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
990 
991   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
992 
993   /*Select the string "wine"*/
994   cr.cpMin = 0;
995   cr.cpMax = 4;
996   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
997 
998   /*Copy the italicized "wine" to the clipboard*/
999   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1000 
1001   /*Reset the formatting to default*/
1002   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1003   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1004   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1005 
1006   /*Clear the text in the control*/
1007   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1008 
1009   /*Switch to Plain Text Mode*/
1010   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1011   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1012 
1013   /*Input "wine" again in normal format*/
1014   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1015 
1016   /*Paste the italicized "wine" into the control*/
1017   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1018 
1019   /*Select a character from the first "wine" string*/
1020   cr.cpMin = 2;
1021   cr.cpMax = 3;
1022   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1023 
1024   /*Retrieve its formatting*/
1025   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1026               (LPARAM) &cf2);
1027 
1028   /*Select a character from the second "wine" string*/
1029   cr.cpMin = 5;
1030   cr.cpMax = 6;
1031   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1032 
1033   /*Retrieve its formatting*/
1034   cf2test.cbSize = sizeof(CHARFORMAT2);
1035   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1036                (LPARAM) &cf2test);
1037 
1038   /*Compare the two formattings*/
1039     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1040       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1041        cf2.dwEffects, cf2test.dwEffects);
1042   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1043                          printing "wine" in the current format(normal)
1044                          pasting "wine" from the clipboard(italicized)
1045                          comparing the two formats(should differ)*/
1046 
1047   /*Attempt to switch with text in control*/
1048   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1049   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1050 
1051   /*Clear control*/
1052   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1053 
1054   /*Switch into Rich Text mode*/
1055   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1056   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1057 
1058   /*Print "wine" in normal formatting into the control*/
1059   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1060 
1061   /*Paste italicized "wine" into the control*/
1062   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1063 
1064   /*Select text from the first "wine" string*/
1065   cr.cpMin = 1;
1066   cr.cpMax = 3;
1067   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1068 
1069   /*Retrieve its formatting*/
1070   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1071                 (LPARAM) &cf2);
1072 
1073   /*Select text from the second "wine" string*/
1074   cr.cpMin = 6;
1075   cr.cpMax = 7;
1076   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1077 
1078   /*Retrieve its formatting*/
1079   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1080                 (LPARAM) &cf2test);
1081 
1082   /*Test that the two formattings are not the same*/
1083   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1084       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1085       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1086 
1087   DestroyWindow(hwndRichEdit);
1088 }
1089 
1090 static void test_TM_PLAINTEXT(void)
1091 {
1092   /*Tests plain text properties*/
1093 
1094   HWND hwndRichEdit = new_richedit(NULL);
1095   CHARFORMAT2 cf2, cf2test;
1096   CHARRANGE cr;
1097   int rc = 0;
1098 
1099   /*Switch to plain text mode*/
1100 
1101   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1102   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1103 
1104   /*Fill control with text*/
1105 
1106   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1107 
1108   /*Select some text and bold it*/
1109 
1110   cr.cpMin = 10;
1111   cr.cpMax = 20;
1112   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113   cf2.cbSize = sizeof(CHARFORMAT2);
1114   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1115               (LPARAM) &cf2);
1116 
1117   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1118   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1119 
1120   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1121   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1122 
1123   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1124   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1125 
1126   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1127   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1128 
1129   /*Get the formatting of those characters*/
1130 
1131   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1132 
1133   /*Get the formatting of some other characters*/
1134   cf2test.cbSize = sizeof(CHARFORMAT2);
1135   cr.cpMin = 21;
1136   cr.cpMax = 30;
1137   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1138   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1139 
1140   /*Test that they are the same as plain text allows only one formatting*/
1141 
1142   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1143      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1144      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1145   
1146   /*Fill the control with a "wine" string, which when inserted will be bold*/
1147 
1148   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1149 
1150   /*Copy the bolded "wine" string*/
1151 
1152   cr.cpMin = 0;
1153   cr.cpMax = 4;
1154   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1155   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1156 
1157   /*Swap back to rich text*/
1158 
1159   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1160   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1161 
1162   /*Set the default formatting to bold italics*/
1163 
1164   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1165   cf2.dwMask |= CFM_ITALIC;
1166   cf2.dwEffects ^= CFE_ITALIC;
1167   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1168   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1169 
1170   /*Set the text in the control to "wine", which will be bold and italicized*/
1171 
1172   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1173 
1174   /*Paste the plain text "wine" string, which should take the insert
1175    formatting, which at the moment is bold italics*/
1176 
1177   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1178 
1179   /*Select the first "wine" string and retrieve its formatting*/
1180 
1181   cr.cpMin = 1;
1182   cr.cpMax = 3;
1183   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1185 
1186   /*Select the second "wine" string and retrieve its formatting*/
1187 
1188   cr.cpMin = 5;
1189   cr.cpMax = 7;
1190   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1191   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1192 
1193   /*Compare the two formattings. They should be the same.*/
1194 
1195   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1196      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1197      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1198   DestroyWindow(hwndRichEdit);
1199 }
1200 
1201 static void test_WM_GETTEXT(void)
1202 {
1203     HWND hwndRichEdit = new_richedit(NULL);
1204     static const char text[] = "Hello. My name is RichEdit!";
1205     static const char text2[] = "Hello. My name is RichEdit!\r";
1206     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1207     char buffer[1024] = {0};
1208     int result;
1209 
1210     /* Baseline test with normal-sized buffer */
1211     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1212     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1213     ok(result == lstrlen(buffer),
1214         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1215     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1216     result = strcmp(buffer,text);
1217     ok(result == 0, 
1218         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1219 
1220     /* Test for returned value of WM_GETTEXTLENGTH */
1221     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1222     ok(result == lstrlen(text),
1223         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1224         result, lstrlen(text));
1225 
1226     /* Test for behavior in overflow case */
1227     memset(buffer, 0, 1024);
1228     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1229     ok(result == 0 ||
1230        result == lstrlenA(text) - 1, /* XP, win2k3 */
1231         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1232     result = strcmp(buffer,text);
1233     if (result)
1234         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1235     ok(result == 0,
1236         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1237 
1238     /* Baseline test with normal-sized buffer and carriage return */
1239     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1240     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1241     ok(result == lstrlen(buffer),
1242         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1243     result = strcmp(buffer,text2_after);
1244     ok(result == 0,
1245         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1246 
1247     /* Test for returned value of WM_GETTEXTLENGTH */
1248     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1249     ok(result == lstrlen(text2_after),
1250         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1251         result, lstrlen(text2_after));
1252 
1253     /* Test for behavior of CRLF conversion in case of overflow */
1254     memset(buffer, 0, 1024);
1255     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1256     ok(result == 0 ||
1257        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1258         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1259     result = strcmp(buffer,text2);
1260     if (result)
1261         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1262     ok(result == 0,
1263         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1264 
1265     DestroyWindow(hwndRichEdit);
1266 }
1267 
1268 static void test_EM_GETTEXTRANGE(void)
1269 {
1270     HWND hwndRichEdit = new_richedit(NULL);
1271     const char * text1 = "foo bar\r\nfoo bar";
1272     const char * text2 = "foo bar\rfoo bar";
1273     const char * expect = "bar\rfoo";
1274     char buffer[1024] = {0};
1275     LRESULT result;
1276     TEXTRANGEA textRange;
1277 
1278     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1279 
1280     textRange.lpstrText = buffer;
1281     textRange.chrg.cpMin = 4;
1282     textRange.chrg.cpMax = 11;
1283     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1284     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1285     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1286 
1287     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1288 
1289     textRange.lpstrText = buffer;
1290     textRange.chrg.cpMin = 4;
1291     textRange.chrg.cpMax = 11;
1292     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1293     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1294     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1295 
1296     DestroyWindow(hwndRichEdit);
1297 }
1298 
1299 static void test_EM_GETSELTEXT(void)
1300 {
1301     HWND hwndRichEdit = new_richedit(NULL);
1302     const char * text1 = "foo bar\r\nfoo bar";
1303     const char * text2 = "foo bar\rfoo bar";
1304     const char * expect = "bar\rfoo";
1305     char buffer[1024] = {0};
1306     LRESULT result;
1307 
1308     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1309 
1310     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1311     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1312     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1313     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1314 
1315     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1316 
1317     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1318     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1319     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1320     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1321 
1322     DestroyWindow(hwndRichEdit);
1323 }
1324 
1325 /* FIXME: need to test unimplemented options and robustly test wparam */
1326 static void test_EM_SETOPTIONS(void)
1327 {
1328     HWND hwndRichEdit = new_richedit(NULL);
1329     static const char text[] = "Hello. My name is RichEdit!";
1330     char buffer[1024] = {0};
1331 
1332     /* NEGATIVE TESTING - NO OPTIONS SET */
1333     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1334     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1335 
1336     /* testing no readonly by sending 'a' to the control*/
1337     SetFocus(hwndRichEdit);
1338     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1339     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340     ok(buffer[0]=='a', 
1341        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1342     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1343 
1344     /* READONLY - sending 'a' to the control */
1345     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1346     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1347     SetFocus(hwndRichEdit);
1348     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1349     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1350     ok(buffer[0]==text[0], 
1351        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1352 
1353     DestroyWindow(hwndRichEdit);
1354 }
1355 
1356 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1357 {
1358   CHARFORMAT2W text_format;
1359   text_format.cbSize = sizeof(text_format);
1360   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1361   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1362   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1363 }
1364 
1365 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1366 {
1367   int link_present = 0;
1368 
1369   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1370   if (is_url) 
1371   { /* control text is url; should get CFE_LINK */
1372         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1373   }
1374   else 
1375   {
1376     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1377   }
1378 }
1379 
1380 static HWND new_static_wnd(HWND parent) {
1381   return new_window("Static", 0, parent);
1382 }
1383 
1384 static void test_EM_AUTOURLDETECT(void)
1385 {
1386   struct urls_s {
1387     const char *text;
1388     int is_url;
1389   } urls[12] = {
1390     {"winehq.org", 0},
1391     {"http://www.winehq.org", 1},
1392     {"http//winehq.org", 0},
1393     {"ww.winehq.org", 0},
1394     {"www.winehq.org", 1},
1395     {"ftp://192.168.1.1", 1},
1396     {"ftp//192.168.1.1", 0},
1397     {"mailto:your@email.com", 1},    
1398     {"prospero:prosperoserver", 1},
1399     {"telnet:test", 1},
1400     {"news:newserver", 1},
1401     {"wais:waisserver", 1}  
1402   };
1403 
1404   int i, j;
1405   int urlRet=-1;
1406   HWND hwndRichEdit, parent;
1407 
1408   /* All of the following should cause the URL to be detected  */
1409   const char * templates_delim[] = {
1410     "This is some text with X on it",
1411     "This is some text with (X) on it",
1412     "This is some text with X\r on it",
1413     "This is some text with ---X--- on it",
1414     "This is some text with \"X\" on it",
1415     "This is some text with 'X' on it",
1416     "This is some text with 'X' on it",
1417     "This is some text with :X: on it",
1418 
1419     "This text ends with X",
1420 
1421     "This is some text with X) on it",
1422     "This is some text with X--- on it",
1423     "This is some text with X\" on it",
1424     "This is some text with X' on it",
1425     "This is some text with X: on it",
1426 
1427     "This is some text with (X on it",
1428     "This is some text with \rX on it",
1429     "This is some text with ---X on it",
1430     "This is some text with \"X on it",
1431     "This is some text with 'X on it",
1432     "This is some text with :X on it",
1433   };
1434   /* None of these should cause the URL to be detected */
1435   const char * templates_non_delim[] = {
1436     "This is some text with |X| 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     "This is some text with @X@ on it",
1443     "This is some text with \\X\\ on it",
1444     "This is some text with |X on it",
1445     "This is some text with *X on it",
1446     "This is some text with /X on it",
1447     "This is some text with +X on it",
1448     "This is some text with %X on it",
1449     "This is some text with #X on it",
1450     "This is some text with @X on it",
1451     "This is some text with \\X on it",
1452   };
1453   /* All of these cause the URL detection to be extended by one more byte,
1454      thus demonstrating that the tested character is considered as part
1455      of the URL. */
1456   const char * templates_xten_delim[] = {
1457     "This is some text with X| on it",
1458     "This is some text with X* on it",
1459     "This is some text with X/ on it",
1460     "This is some text with X+ on it",
1461     "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   };
1466   char buffer[1024];
1467 
1468   parent = new_static_wnd(NULL);
1469   hwndRichEdit = new_richedit(parent);
1470   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1471   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1472   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1473   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1474   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1475   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1476   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1477   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1478   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1479   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1480   /* for each url, check the text to see if CFE_LINK effect is present */
1481   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1482 
1483     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1484     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1485     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1486 
1487     /* Link detection should happen immediately upon WM_SETTEXT */
1488     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1489     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1490     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1491   }
1492   DestroyWindow(hwndRichEdit);
1493 
1494   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1495   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1496     hwndRichEdit = new_richedit(parent);
1497 
1498     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1499       char * at_pos;
1500       int at_offset;
1501       int end_offset;
1502 
1503       at_pos = strchr(templates_delim[j], 'X');
1504       at_offset = at_pos - templates_delim[j];
1505       strncpy(buffer, templates_delim[j], at_offset);
1506       buffer[at_offset] = '\0';
1507       strcat(buffer, urls[i].text);
1508       strcat(buffer, templates_delim[j] + at_offset + 1);
1509       end_offset = at_offset + strlen(urls[i].text);
1510 
1511       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1512       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1513 
1514       /* This assumes no templates start with the URL itself, and that they
1515          have at least two characters before the URL text */
1516       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1517         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1518       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1519         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1520       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1521         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1522 
1523       if (urls[i].is_url)
1524       {
1525         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1526           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1527         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1528           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1529       }
1530       else
1531       {
1532         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1533           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1534         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1535           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1536       }
1537       if (buffer[end_offset] != '\0')
1538       {
1539         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1540           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1541         if (buffer[end_offset +1] != '\0')
1542         {
1543           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1544             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1545         }
1546       }
1547     }
1548 
1549     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1550       char * at_pos;
1551       int at_offset;
1552       int end_offset;
1553 
1554       at_pos = strchr(templates_non_delim[j], 'X');
1555       at_offset = at_pos - templates_non_delim[j];
1556       strncpy(buffer, templates_non_delim[j], at_offset);
1557       buffer[at_offset] = '\0';
1558       strcat(buffer, urls[i].text);
1559       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1560       end_offset = at_offset + strlen(urls[i].text);
1561 
1562       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1563       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1564 
1565       /* This assumes no templates start with the URL itself, and that they
1566          have at least two characters before the URL text */
1567       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1568         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1569       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1570         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1571       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1572         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1573 
1574       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1575         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1576       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1577         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1578       if (buffer[end_offset] != '\0')
1579       {
1580         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1581           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1582         if (buffer[end_offset +1] != '\0')
1583         {
1584           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1585             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1586         }
1587       }
1588     }
1589 
1590     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1591       char * at_pos;
1592       int at_offset;
1593       int end_offset;
1594 
1595       at_pos = strchr(templates_xten_delim[j], 'X');
1596       at_offset = at_pos - templates_xten_delim[j];
1597       strncpy(buffer, templates_xten_delim[j], at_offset);
1598       buffer[at_offset] = '\0';
1599       strcat(buffer, urls[i].text);
1600       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1601       end_offset = at_offset + strlen(urls[i].text);
1602 
1603       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1604       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1605 
1606       /* This assumes no templates start with the URL itself, and that they
1607          have at least two characters before the URL text */
1608       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1609         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1610       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1611         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1612       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1613         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1614 
1615       if (urls[i].is_url)
1616       {
1617         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1618           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1619         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1620           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1621         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1622           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1623       }
1624       else
1625       {
1626         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1627           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1628         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1629           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1630         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1631           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1632       }
1633       if (buffer[end_offset +1] != '\0')
1634       {
1635         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1636           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1637         if (buffer[end_offset +2] != '\0')
1638         {
1639           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1640             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1641         }
1642       }
1643     }
1644 
1645     DestroyWindow(hwndRichEdit);
1646     hwndRichEdit = NULL;
1647   }
1648 
1649   /* Test detection of URLs within normal text - WM_CHAR case. */
1650   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1651     hwndRichEdit = new_richedit(parent);
1652 
1653     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1654       char * at_pos;
1655       int at_offset;
1656       int end_offset;
1657       int u, v;
1658 
1659       at_pos = strchr(templates_delim[j], 'X');
1660       at_offset = at_pos - templates_delim[j];
1661       end_offset = at_offset + strlen(urls[i].text);
1662 
1663       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1664       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1665       for (u = 0; templates_delim[j][u]; u++) {
1666         if (templates_delim[j][u] == '\r') {
1667           simulate_typing_characters(hwndRichEdit, "\r");
1668         } else if (templates_delim[j][u] != 'X') {
1669           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1670         } else {
1671           for (v = 0; urls[i].text[v]; v++) {
1672             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1673           }
1674         }
1675       }
1676       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1677       trace("Using template: %s\n", templates_delim[j]);
1678 
1679       /* This assumes no templates start with the URL itself, and that they
1680          have at least two characters before the URL text */
1681       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1682         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1683       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1684         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1685       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1686         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1687 
1688       if (urls[i].is_url)
1689       {
1690         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1691           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1692         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1693           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1694       }
1695       else
1696       {
1697         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1698           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1699         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1700           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1701       }
1702       if (buffer[end_offset] != '\0')
1703       {
1704         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1705           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1706         if (buffer[end_offset +1] != '\0')
1707         {
1708           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1709             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1710         }
1711       }
1712 
1713       /* The following will insert a paragraph break after the first character
1714          of the URL candidate, thus breaking the URL. It is expected that the
1715          CFE_LINK attribute should break across both pieces of the URL */
1716       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1717       simulate_typing_characters(hwndRichEdit, "\r");
1718       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1719 
1720       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1721         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1722       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1723         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1724       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1725         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1726 
1727       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1728         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1729       /* end_offset moved because of paragraph break */
1730       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1731         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1732       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1733       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
1734       {
1735         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1736           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1737         if (buffer[end_offset +2] != '\0')
1738         {
1739           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1740             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1741         }
1742       }
1743 
1744       /* The following will remove the just-inserted paragraph break, thus
1745          restoring the URL */
1746       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1747       simulate_typing_characters(hwndRichEdit, "\b");
1748       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1749 
1750       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1751         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1752       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1753         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1754       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1755         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1756 
1757       if (urls[i].is_url)
1758       {
1759         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1760           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1761         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1762           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1763       }
1764       else
1765       {
1766         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1767           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1768         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1769           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1770       }
1771       if (buffer[end_offset] != '\0')
1772       {
1773         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1774           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1775         if (buffer[end_offset +1] != '\0')
1776         {
1777           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1778             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1779         }
1780       }
1781     }
1782     DestroyWindow(hwndRichEdit);
1783     hwndRichEdit = NULL;
1784   }
1785 
1786   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1787   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1788     SETTEXTEX st;
1789 
1790     hwndRichEdit = new_richedit(parent);
1791 
1792     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1793        be detected:
1794        1) Set entire text, a la WM_SETTEXT
1795        2) Set a selection of the text to the URL
1796        3) Set a portion of the text at a time, which eventually results in
1797           an URL
1798        All of them should give equivalent results
1799      */
1800 
1801     /* Set entire text in one go, like WM_SETTEXT */
1802     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1803       char * at_pos;
1804       int at_offset;
1805       int end_offset;
1806 
1807       st.codepage = CP_ACP;
1808       st.flags = ST_DEFAULT;
1809 
1810       at_pos = strchr(templates_delim[j], 'X');
1811       at_offset = at_pos - templates_delim[j];
1812       strncpy(buffer, templates_delim[j], at_offset);
1813       buffer[at_offset] = '\0';
1814       strcat(buffer, urls[i].text);
1815       strcat(buffer, templates_delim[j] + at_offset + 1);
1816       end_offset = at_offset + strlen(urls[i].text);
1817 
1818       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1819       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1820 
1821       /* This assumes no templates start with the URL itself, and that they
1822          have at least two characters before the URL text */
1823       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1824         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1825       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1826         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1827       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1828         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1829 
1830       if (urls[i].is_url)
1831       {
1832         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1833           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1834         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1835           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1836       }
1837       else
1838       {
1839         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1840           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1841         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1842           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1843       }
1844       if (buffer[end_offset] != '\0')
1845       {
1846         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1847           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1848         if (buffer[end_offset +1] != '\0')
1849         {
1850           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1851             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1852         }
1853       }
1854     }
1855 
1856     /* Set selection with X to the URL */
1857     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1858       char * at_pos;
1859       int at_offset;
1860       int end_offset;
1861 
1862       at_pos = strchr(templates_delim[j], 'X');
1863       at_offset = at_pos - templates_delim[j];
1864       end_offset = at_offset + strlen(urls[i].text);
1865 
1866       st.codepage = CP_ACP;
1867       st.flags = ST_DEFAULT;
1868       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1869       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1870       st.flags = ST_SELECTION;
1871       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1872       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1873       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1874 
1875       /* This assumes no templates start with the URL itself, and that they
1876          have at least two characters before the URL text */
1877       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1878         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1879       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1880         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1881       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1882         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1883 
1884       if (urls[i].is_url)
1885       {
1886         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1887           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1888         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1889           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1890       }
1891       else
1892       {
1893         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1894           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1895         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1896           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1897       }
1898       if (buffer[end_offset] != '\0')
1899       {
1900         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1901           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1902         if (buffer[end_offset +1] != '\0')
1903         {
1904           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1905             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1906         }
1907       }
1908     }
1909 
1910     /* Set selection with X to the first character of the URL, then the rest */
1911     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1912       char * at_pos;
1913       int at_offset;
1914       int end_offset;
1915 
1916       at_pos = strchr(templates_delim[j], 'X');
1917       at_offset = at_pos - templates_delim[j];
1918       end_offset = at_offset + strlen(urls[i].text);
1919 
1920       strcpy(buffer, "YY");
1921       buffer[0] = urls[i].text[0];
1922 
1923       st.codepage = CP_ACP;
1924       st.flags = ST_DEFAULT;
1925       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1926       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1927       st.flags = ST_SELECTION;
1928       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1929       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1930       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1931       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1932       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1933 
1934       /* This assumes no templates start with the URL itself, and that they
1935          have at least two characters before the URL text */
1936       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1937         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1938       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1939         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1940       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1941         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1942 
1943       if (urls[i].is_url)
1944       {
1945         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1946           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1947         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1948           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1949       }
1950       else
1951       {
1952         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1953           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1954         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1955           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1956       }
1957       if (buffer[end_offset] != '\0')
1958       {
1959         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1960           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1961         if (buffer[end_offset +1] != '\0')
1962         {
1963           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1964             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1965         }
1966       }
1967     }
1968 
1969     DestroyWindow(hwndRichEdit);
1970     hwndRichEdit = NULL;
1971   }
1972 
1973   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1974   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1975     hwndRichEdit = new_richedit(parent);
1976 
1977     /* Set selection with X to the URL */
1978     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1979       char * at_pos;
1980       int at_offset;
1981       int end_offset;
1982 
1983       at_pos = strchr(templates_delim[j], 'X');
1984       at_offset = at_pos - templates_delim[j];
1985       end_offset = at_offset + strlen(urls[i].text);
1986 
1987       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1988       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1989       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1990       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1991       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1992 
1993       /* This assumes no templates start with the URL itself, and that they
1994          have at least two characters before the URL text */
1995       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1996         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1997       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1998         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1999       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2000         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2001 
2002       if (urls[i].is_url)
2003       {
2004         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2005           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2006         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2007           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2008       }
2009       else
2010       {
2011         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2012           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2013         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2014           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015       }
2016       if (buffer[end_offset] != '\0')
2017       {
2018         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2019           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2020         if (buffer[end_offset +1] != '\0')
2021         {
2022           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2023             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2024         }
2025       }
2026     }
2027 
2028     /* Set selection with X to the first character of the URL, then the rest */
2029     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2030       char * at_pos;
2031       int at_offset;
2032       int end_offset;
2033 
2034       at_pos = strchr(templates_delim[j], 'X');
2035       at_offset = at_pos - templates_delim[j];
2036       end_offset = at_offset + strlen(urls[i].text);
2037 
2038       strcpy(buffer, "YY");
2039       buffer[0] = urls[i].text[0];
2040 
2041       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2042       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2043       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2044       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2045       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2046       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2047       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2048 
2049       /* This assumes no templates start with the URL itself, and that they
2050          have at least two characters before the URL text */
2051       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2052         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2053       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2054         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2055       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2056         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2057 
2058       if (urls[i].is_url)
2059       {
2060         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2061           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2062         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2063           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2064       }
2065       else
2066       {
2067         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2068           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2069         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2070           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2071       }
2072       if (buffer[end_offset] != '\0')
2073       {
2074         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2075           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2076         if (buffer[end_offset +1] != '\0')
2077         {
2078           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2079             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2080         }
2081       }
2082     }
2083 
2084     DestroyWindow(hwndRichEdit);
2085     hwndRichEdit = NULL;
2086   }
2087 
2088   DestroyWindow(parent);
2089 }
2090 
2091 static void test_EM_SCROLL(void)
2092 {
2093   int i, j;
2094   int r; /* return value */
2095   int expr; /* expected return value */
2096   HWND hwndRichEdit = new_richedit(NULL);
2097   int y_before, y_after; /* units of lines of text */
2098 
2099   /* test a richedit box containing a single line of text */
2100   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2101   expr = 0x00010000;
2102   for (i = 0; i < 4; i++) {
2103     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2104 
2105     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2106     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2107     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2108        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2109     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2110        "(i == %d)\n", y_after, i);
2111   }
2112 
2113   /*
2114    * test a richedit box that will scroll. There are two general
2115    * cases: the case without any long lines and the case with a long
2116    * line.
2117    */
2118   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2119     if (i == 0)
2120       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2121     else
2122       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2123                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2124                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2125                   "LONG LINE \nb\nc\nd\ne");
2126     for (j = 0; j < 12; j++) /* reset scroll position to top */
2127       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2128 
2129     /* get first visible line */
2130     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2131     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2132 
2133     /* get new current first visible line */
2134     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2135 
2136     ok(((r & 0xffffff00) == 0x00010000) &&
2137        ((r & 0x000000ff) != 0x00000000),
2138        "EM_SCROLL page down didn't scroll by a small positive number of "
2139        "lines (r == 0x%08x)\n", r);
2140     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2141        "(line %d scrolled to line %d\n", y_before, y_after);
2142 
2143     y_before = y_after;
2144     
2145     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2146     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2147     ok(((r & 0xffffff00) == 0x0001ff00),
2148        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2149        "(r == 0x%08x)\n", r);
2150     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2151        "%d scrolled to line %d\n", y_before, y_after);
2152     
2153     y_before = y_after;
2154 
2155     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2156 
2157     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2158 
2159     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2160        "(r == 0x%08x)\n", r);
2161     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2162        "1 line (%d scrolled to %d)\n", y_before, y_after);
2163 
2164     y_before = y_after;
2165 
2166     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2167 
2168     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2169 
2170     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2171        "(r == 0x%08x)\n", r);
2172     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2173        "line (%d scrolled to %d)\n", y_before, y_after);
2174 
2175     y_before = y_after;
2176 
2177     r = SendMessage(hwndRichEdit, EM_SCROLL,
2178                     SB_LINEUP, 0); /* lineup beyond top */
2179 
2180     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2181 
2182     ok(r == 0x00010000,
2183        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2184     ok(y_before == y_after,
2185        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2186 
2187     y_before = y_after;
2188 
2189     r = SendMessage(hwndRichEdit, EM_SCROLL,
2190                     SB_PAGEUP, 0);/*page up beyond top */
2191 
2192     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2193 
2194     ok(r == 0x00010000,
2195        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2196     ok(y_before == y_after,
2197        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2198 
2199     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2200       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2201     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2202     r = SendMessage(hwndRichEdit, EM_SCROLL,
2203                     SB_PAGEDOWN, 0); /* page down beyond bot */
2204     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2205 
2206     ok(r == 0x00010000,
2207        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2208     ok(y_before == y_after,
2209        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2210        y_before, y_after);
2211 
2212     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213     SendMessage(hwndRichEdit, EM_SCROLL,
2214                 SB_LINEDOWN, 0); /* line down beyond bot */
2215     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2216     
2217     ok(r == 0x00010000,
2218        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2219     ok(y_before == y_after,
2220        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2221        y_before, y_after);
2222   }
2223   DestroyWindow(hwndRichEdit);
2224 }
2225 
2226 static void test_EM_SETUNDOLIMIT(void)
2227 {
2228   /* cases we test for:
2229    * default behaviour - limiting at 100 undo's 
2230    * undo disabled - setting a limit of 0
2231    * undo limited -  undo limit set to some to some number, like 2
2232    * bad input - sending a negative number should default to 100 undo's */
2233  
2234   HWND hwndRichEdit = new_richedit(NULL);
2235   CHARRANGE cr;
2236   int i;
2237   int result;
2238   
2239   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2240   cr.cpMin = 0;
2241   cr.cpMax = 1;
2242   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2243     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2244       also, multiple pastes don't combine like WM_CHAR would */
2245   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2246 
2247   /* first case - check the default */
2248   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
2249   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2250     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
2251   for (i=0; i<100; i++) /* Undo 100 of them */
2252     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
2253   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2254      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2255 
2256   /* second case - cannot undo */
2257   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
2258   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
2259   SendMessage(hwndRichEdit,
2260               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2261   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2262      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2263 
2264   /* third case - set it to an arbitrary number */
2265   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
2266   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
2267   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2268   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2269   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
2270   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2271   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2272      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2273   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2274   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2275      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2276   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
2277   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2278      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2279   
2280   /* fourth case - setting negative numbers should default to 100 undos */
2281   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
2282   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2283   ok (result == 100, 
2284       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2285       
2286   DestroyWindow(hwndRichEdit);
2287 }
2288 
2289 static void test_ES_PASSWORD(void)
2290 {
2291   /* This isn't hugely testable, so we're just going to run it through its paces */
2292 
2293   HWND hwndRichEdit = new_richedit(NULL);
2294   WCHAR result;
2295 
2296   /* First, check the default of a regular control */
2297   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2298   ok (result == 0,
2299         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2300 
2301   /* Now, set it to something normal */
2302   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2303   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2304   ok (result == 120,
2305         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2306 
2307   /* Now, set it to something odd */
2308   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2309   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2310   ok (result == 1234,
2311         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2312   DestroyWindow(hwndRichEdit);
2313 }
2314 
2315 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2316                                          LPBYTE pbBuff,
2317                                          LONG cb,
2318                                          LONG *pcb)
2319 {
2320   char** str = (char**)dwCookie;
2321   *pcb = cb;
2322   if (*pcb > 0) {
2323     memcpy(*str, pbBuff, *pcb);
2324     *str += *pcb;
2325   }
2326   return 0;
2327 }
2328 
2329 static void test_WM_SETTEXT()
2330 {
2331   HWND hwndRichEdit = new_richedit(NULL);
2332   const char * TestItem1 = "TestSomeText";
2333   const char * TestItem2 = "TestSomeText\r";
2334   const char * TestItem2_after = "TestSomeText\r\n";
2335   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2336   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2337   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2338   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2339   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2340   const char * TestItem5_after = "TestSomeText TestSomeText";
2341   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2342   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2343   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2344   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2345 
2346   char buf[1024] = {0};
2347   LRESULT result;
2348   EDITSTREAM es;
2349   char * p;
2350 
2351   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2352      any solitary \r to be converted to \r\n on return. Properly paired
2353      \r\n are not affected. It also shows that the special sequence \r\r\n
2354      gets converted to a single space.
2355    */
2356 
2357 #define TEST_SETTEXT(a, b) \
2358   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2359   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2360   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2361   ok (result == lstrlen(buf), \
2362         "WM_GETTEXT returned %ld instead of expected %u\n", \
2363         result, lstrlen(buf)); \
2364   result = strcmp(b, buf); \
2365   ok(result == 0, \
2366         "WM_SETTEXT round trip: strcmp = %ld\n", result);
2367 
2368   TEST_SETTEXT(TestItem1, TestItem1)
2369   TEST_SETTEXT(TestItem2, TestItem2_after)
2370   TEST_SETTEXT(TestItem3, TestItem3_after)
2371   TEST_SETTEXT(TestItem3_after, TestItem3_after)
2372   TEST_SETTEXT(TestItem4, TestItem4_after)
2373   TEST_SETTEXT(TestItem5, TestItem5_after)
2374   TEST_SETTEXT(TestItem6, TestItem6_after)
2375   TEST_SETTEXT(TestItem7, TestItem7_after)
2376 
2377   /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2378   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2379   p = buf;
2380   es.dwCookie = (DWORD_PTR)&p;
2381   es.dwError = 0;
2382   es.pfnCallback = test_WM_SETTEXT_esCallback;
2383   memset(buf, 0, sizeof(buf));
2384   SendMessage(hwndRichEdit, EM_STREAMOUT,
2385               (WPARAM)(SF_RTF), (LPARAM)&es);
2386   trace("EM_STREAMOUT produced: \n%s\n", buf);
2387   TEST_SETTEXT(buf, TestItem1)
2388 
2389 #undef TEST_SETTEXT
2390   DestroyWindow(hwndRichEdit);
2391 }
2392 
2393 static void test_EM_STREAMOUT(void)
2394 {
2395   HWND hwndRichEdit = new_richedit(NULL);
2396   int r;
2397   EDITSTREAM es;
2398   char buf[1024] = {0};
2399   char * p;
2400 
2401   const char * TestItem1 = "TestSomeText";
2402   const char * TestItem2 = "TestSomeText\r";
2403   const char * TestItem3 = "TestSomeText\r\n";
2404 
2405   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2406   p = buf;
2407   es.dwCookie = (DWORD_PTR)&p;
2408   es.dwError = 0;
2409   es.pfnCallback = test_WM_SETTEXT_esCallback;
2410   memset(buf, 0, sizeof(buf));
2411   SendMessage(hwndRichEdit, EM_STREAMOUT,
2412               (WPARAM)(SF_TEXT), (LPARAM)&es);
2413   r = strlen(buf);
2414   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2415   ok(strcmp(buf, TestItem1) == 0,
2416         "streamed text different, got %s\n", buf);
2417 
2418   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2419   p = buf;
2420   es.dwCookie = (DWORD_PTR)&p;
2421   es.dwError = 0;
2422   es.pfnCallback = test_WM_SETTEXT_esCallback;
2423   memset(buf, 0, sizeof(buf));
2424   SendMessage(hwndRichEdit, EM_STREAMOUT,
2425               (WPARAM)(SF_TEXT), (LPARAM)&es);
2426   r = strlen(buf);
2427   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2428   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2429   ok(strcmp(buf, TestItem3) == 0,
2430         "streamed text different from, got %s\n", buf);
2431   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2432   p = buf;
2433   es.dwCookie = (DWORD_PTR)&p;
2434   es.dwError = 0;
2435   es.pfnCallback = test_WM_SETTEXT_esCallback;
2436   memset(buf, 0, sizeof(buf));
2437   SendMessage(hwndRichEdit, EM_STREAMOUT,
2438               (WPARAM)(SF_TEXT), (LPARAM)&es);
2439   r = strlen(buf);
2440   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2441   ok(strcmp(buf, TestItem3) == 0,
2442         "streamed text different, got %s\n", buf);
2443 
2444   DestroyWindow(hwndRichEdit);
2445 }
2446 
2447 static void test_EM_SETTEXTEX(void)
2448 {
2449   HWND hwndRichEdit = new_richedit(NULL);
2450   SETTEXTEX setText;
2451   GETTEXTEX getText;
2452   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
2453                        'S', 'o', 'm', 'e', 
2454                        'T', 'e', 'x', 't', 0}; 
2455   WCHAR TestItem2[] = {'T', 'e', 's', 't',
2456                        'S', 'o', 'm', 'e',
2457                        'T', 'e', 'x', 't',
2458                       '\r', 0};
2459   const char * TestItem2_after = "TestSomeText\r\n";
2460   WCHAR TestItem3[] = {'T', 'e', 's', 't',
2461                        'S', 'o', 'm', 'e',
2462                        'T', 'e', 'x', 't',
2463                       '\r','\n','\r','\n', 0};
2464   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2465                        'S', 'o', 'm', 'e',
2466                        'T', 'e', 'x', 't',
2467                        '\n','\n', 0};
2468   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2469                        'S', 'o', 'm', 'e',
2470                        'T', 'e', 'x', 't',
2471                        '\r','\r', 0};
2472   WCHAR TestItem4[] = {'T', 'e', 's', 't',
2473                        'S', 'o', 'm', 'e',
2474                        'T', 'e', 'x', 't',
2475                       '\r','\r','\n','\r',
2476                       '\n', 0};
2477   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2478                        'S', 'o', 'm', 'e',
2479                        'T', 'e', 'x', 't',
2480                        ' ','\r', 0};
2481 #define MAX_BUF_LEN 1024
2482   WCHAR buf[MAX_BUF_LEN];
2483   char * p;
2484   int result;
2485   CHARRANGE cr;
2486   EDITSTREAM es;
2487 
2488   setText.codepage = 1200;  /* no constant for unicode */
2489   getText.codepage = 1200;  /* no constant for unicode */
2490   getText.cb = MAX_BUF_LEN;
2491   getText.flags = GT_DEFAULT;
2492   getText.lpDefaultChar = NULL;
2493   getText.lpUsedDefChar = NULL;
2494 
2495   setText.flags = 0;
2496   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2497   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2498   ok(lstrcmpW(buf, TestItem1) == 0,
2499       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2500 
2501   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2502      convert \r to \r\n on return
2503    */
2504   setText.codepage = 1200;  /* no constant for unicode */
2505   getText.codepage = 1200;  /* no constant for unicode */
2506   getText.cb = MAX_BUF_LEN;
2507   getText.flags = GT_DEFAULT;
2508   getText.lpDefaultChar = NULL;
2509   getText.lpUsedDefChar = NULL;
2510   setText.flags = 0;
2511   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2512   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2513   ok(lstrcmpW(buf, TestItem2) == 0,
2514       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2515 
2516   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2517   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2518   ok(strcmp((const char *)buf, TestItem2_after) == 0,
2519       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2520 
2521   /* Baseline test for just-enough buffer space for string */
2522   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2523   getText.codepage = 1200;  /* no constant for unicode */
2524   getText.flags = GT_DEFAULT;
2525   getText.lpDefaultChar = NULL;
2526   getText.lpUsedDefChar = NULL;
2527   memset(buf, 0, MAX_BUF_LEN);
2528   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2529   ok(lstrcmpW(buf, TestItem2) == 0,
2530       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2531 
2532   /* When there is enough space for one character, but not both, of the CRLF
2533      pair at the end of the string, the CR is not copied at all. That is,
2534      the caller must not see CRLF pairs truncated to CR at the end of the
2535      string.
2536    */
2537   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2538   getText.codepage = 1200;  /* no constant for unicode */
2539   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
2540   getText.lpDefaultChar = NULL;
2541   getText.lpUsedDefChar = NULL;
2542   memset(buf, 0, MAX_BUF_LEN);
2543   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2544   ok(lstrcmpW(buf, TestItem1) == 0,
2545       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2546 
2547 
2548   /* \r\n pairs get changed into \r */
2549   setText.codepage = 1200;  /* no constant for unicode */
2550   getText.codepage = 1200;  /* no constant for unicode */
2551   getText.cb = MAX_BUF_LEN;
2552   getText.flags = GT_DEFAULT;
2553   getText.lpDefaultChar = NULL;
2554   getText.lpUsedDefChar = NULL;
2555   setText.flags = 0;
2556   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2557   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2558   ok(lstrcmpW(buf, TestItem3_after) == 0,
2559       "EM_SETTEXTEX did not convert properly\n");
2560 
2561   /* \n also gets changed to \r */
2562   setText.codepage = 1200;  /* no constant for unicode */
2563   getText.codepage = 1200;  /* no constant for unicode */
2564   getText.cb = MAX_BUF_LEN;
2565   getText.flags = GT_DEFAULT;
2566   getText.lpDefaultChar = NULL;
2567   getText.lpUsedDefChar = NULL;
2568   setText.flags = 0;
2569   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2570   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2571   ok(lstrcmpW(buf, TestItem3_after) == 0,
2572       "EM_SETTEXTEX did not convert properly\n");
2573 
2574   /* \r\r\n gets changed into single space */
2575   setText.codepage = 1200;  /* no constant for unicode */
2576   getText.codepage = 1200;  /* no constant for unicode */
2577   getText.cb = MAX_BUF_LEN;
2578   getText.flags = GT_DEFAULT;
2579   getText.lpDefaultChar = NULL;
2580   getText.lpUsedDefChar = NULL;
2581   setText.flags = 0;
2582   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2583   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2584   ok(lstrcmpW(buf, TestItem4_after) == 0,
2585       "EM_SETTEXTEX did not convert properly\n");
2586 
2587   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
2588                        (WPARAM)&setText, (LPARAM) NULL);
2589   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2590   
2591   ok (result == 1, 
2592       "EM_SETTEXTEX returned %d, instead of 1\n",result);
2593   ok(lstrlenW(buf) == 0,
2594       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2595   
2596   /* put some text back */
2597   setText.flags = 0;
2598   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2599   /* select some text */
2600   cr.cpMax = 1;
2601   cr.cpMin = 3;
2602   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2603   /* replace current selection */
2604   setText.flags = ST_SELECTION;
2605   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
2606                        (WPARAM)&setText, (LPARAM) NULL);
2607   ok(result == 0,
2608       "EM_SETTEXTEX with NULL lParam to replace selection"
2609       " with no text should return 0. Got %i\n",
2610       result);
2611   
2612   /* put some text back */
2613   setText.flags = 0;
2614   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2615   /* select some text */
2616   cr.cpMax = 1;
2617   cr.cpMin = 3;
2618   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2619   /* replace current selection */
2620   setText.flags = ST_SELECTION;
2621   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2622                        (WPARAM)&setText, (LPARAM) TestItem1);
2623   /* get text */
2624   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2625   ok(result == lstrlenW(TestItem1),
2626       "EM_SETTEXTEX with NULL lParam to replace selection"
2627       " with no text should return 0. Got %i\n",
2628       result);
2629   ok(lstrlenW(buf) == 22,
2630       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2631       lstrlenW(buf) );
2632 
2633   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2634   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2635   p = (char *)buf;
2636   es.dwCookie = (DWORD_PTR)&p;
2637   es.dwError = 0;
2638   es.pfnCallback = test_WM_SETTEXT_esCallback;
2639   memset(buf, 0, sizeof(buf));
2640   SendMessage(hwndRichEdit, EM_STREAMOUT,
2641               (WPARAM)(SF_RTF), (LPARAM)&es);
2642   trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2643 
2644   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2645   getText.codepage = 1200;  /* no constant for unicode */
2646   getText.cb = MAX_BUF_LEN;
2647   getText.flags = GT_DEFAULT;
2648   getText.lpDefaultChar = NULL;
2649   getText.lpUsedDefChar = NULL;
2650 
2651   setText.flags = 0;
2652   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2653   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2654   ok(lstrcmpW(buf, TestItem1) == 0,
2655       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2656 
2657 
2658   DestroyWindow(hwndRichEdit);
2659 }
2660 
2661 static void test_EM_LIMITTEXT(void)
2662 {
2663   int ret;
2664 
2665   HWND hwndRichEdit = new_richedit(NULL);
2666 
2667   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2668    * about setting the length to -1 for multiline edit controls doesn't happen.
2669    */
2670 
2671   /* Don't check default gettextlimit case. That's done in other tests */
2672 
2673   /* Set textlimit to 100 */
2674   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2675   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2676   ok (ret == 100,
2677       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2678 
2679   /* Set textlimit to 0 */
2680   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2681   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2682   ok (ret == 65536,
2683       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2684 
2685   /* Set textlimit to -1 */
2686   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2687   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2688   ok (ret == -1,
2689       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2690 
2691   /* Set textlimit to -2 */
2692   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2693   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2694   ok (ret == -2,
2695       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2696 
2697   DestroyWindow (hwndRichEdit);
2698 }
2699 
2700 
2701 static void test_EM_EXLIMITTEXT(void)
2702 {
2703   int i, selBegin, selEnd, len1, len2;
2704   int result;
2705   char text[1024 + 1];
2706   char buffer[1024 + 1];
2707   int textlimit = 0; /* multiple of 100 */
2708   HWND hwndRichEdit = new_richedit(NULL);
2709   
2710   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2711   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2712   
2713   textlimit = 256000;
2714   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2715   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2716   /* set higher */
2717   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2718   
2719   textlimit = 1000;
2720   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2721   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2722   /* set lower */
2723   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2724  
2725   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2726   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2727   /* default for WParam = 0 */
2728   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2729  
2730   textlimit = sizeof(text)-1;
2731   memset(text, 'W', textlimit);
2732   text[sizeof(text)-1] = 0;
2733   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2734   /* maxed out text */
2735   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2736   
2737   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
2738   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2739   len1 = selEnd - selBegin;
2740   
2741   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2742   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2743   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2744   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2745   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2746   len2 = selEnd - selBegin;
2747   
2748   ok(len1 != len2,
2749     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2750     len1,len2,i);
2751   
2752   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2753   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2754   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2755   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2756   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2757   len1 = selEnd - selBegin;
2758   
2759   ok(len1 != len2,
2760     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2761     len1,len2,i);
2762   
2763   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2764   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2765   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
2766   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2767   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2768   len2 = selEnd - selBegin;
2769   
2770   ok(len1 == len2, 
2771     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2772     len1,len2,i);
2773 
2774   /* set text up to the limit, select all the text, then add a char */
2775   textlimit = 5;
2776   memset(text, 'W', textlimit);
2777   text[textlimit] = 0;
2778   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2779   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2780   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2781   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2782   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2783   result = strcmp(buffer, "A");
2784   ok(0 == result, "got string = \"%s\"\n", buffer);
2785 
2786   /* WM_SETTEXT not limited */
2787   textlimit = 10;
2788   memset(text, 'W', textlimit);
2789   text[textlimit] = 0;
2790   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2791   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2792   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2793   i = strlen(buffer);
2794   ok(10 == i, "expected 10 chars\n");
2795   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2796   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2797 
2798   /* try inserting more text at end */
2799   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2800   ok(0 == i, "WM_CHAR wasn't processed\n");
2801   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2802   i = strlen(buffer);
2803   ok(10 == i, "expected 10 chars, got %i\n", i);
2804   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2805   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2806 
2807   /* try inserting text at beginning */
2808   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2809   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2810   ok(0 == i, "WM_CHAR wasn't processed\n");
2811   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2812   i = strlen(buffer);
2813   ok(10 == i, "expected 10 chars, got %i\n", i);
2814   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2815   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2816 
2817   /* WM_CHAR is limited */
2818   textlimit = 1;
2819   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2820   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
2821   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2822   ok(0 == i, "WM_CHAR wasn't processed\n");
2823   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2824   ok(0 == i, "WM_CHAR wasn't processed\n");
2825   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2826   i = strlen(buffer);
2827   ok(1 == i, "expected 1 chars, got %i instead\n", i);
2828 
2829   DestroyWindow(hwndRichEdit);
2830 }
2831 
2832 static void test_EM_GETLIMITTEXT(void)
2833 {
2834   int i;
2835   HWND hwndRichEdit = new_richedit(NULL);
2836 
2837   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2838   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2839 
2840   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2841   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2842   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2843 
2844   DestroyWindow(hwndRichEdit);
2845 }
2846 
2847 static void test_WM_SETFONT(void)
2848 {
2849   /* There is no invalid input or error conditions for this function.
2850    * NULL wParam and lParam just fall back to their default values 
2851    * It should be noted that even if you use a gibberish name for your fonts
2852    * here, it will still work because the name is stored. They will display as
2853    * System, but will report their name to be whatever they were created as */
2854   
2855   HWND hwndRichEdit = new_richedit(NULL);
2856   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2857     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2858     FF_DONTCARE, "Marlett");
2859   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2860     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2861     FF_DONTCARE, "MS Sans Serif");
2862   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2863     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2864     FF_DONTCARE, "Courier");
2865   LOGFONTA sentLogFont;
2866   CHARFORMAT2A returnedCF2A;
2867   
2868   returnedCF2A.cbSize = sizeof(returnedCF2A);
2869   
2870   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2871   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2872   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2873 
2874   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2875   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2876     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2877     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2878 
2879   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2880   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2881   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2882   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2883     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2884     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2885     
2886   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2887   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2888   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2889   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2890     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2891     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2892    
2893   /* This last test is special since we send in NULL. We clear the variables
2894    * and just compare to "System" instead of the sent in font name. */
2895   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2896   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2897   returnedCF2A.cbSize = sizeof(returnedCF2A);
2898   
2899   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2900   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2901   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2902   ok (!strcmp("System",returnedCF2A.szFaceName),
2903     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2904   
2905   DestroyWindow(hwndRichEdit);
2906 }
2907 
2908 
2909 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2910                                          LPBYTE pbBuff,
2911                                          LONG cb,
2912                                          LONG *pcb)
2913 {
2914   const char** str = (const char**)dwCookie;
2915   int size = strlen(*str);
2916   if(size > 3)  /* let's make it piecemeal for fun */
2917     size = 3;
2918   *pcb = cb;
2919   if (*pcb > size) {
2920     *pcb = size;
2921   }
2922   if (*pcb > 0) {
2923     memcpy(pbBuff, *str, *pcb);
2924     *str += *pcb;
2925   }
2926   return 0;
2927 }
2928 
2929 static void test_EM_GETMODIFY(void)
2930 {
2931   HWND hwndRichEdit = new_richedit(NULL);
2932   LRESULT result;
2933   SETTEXTEX setText;
2934   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
2935                        'S', 'o', 'm', 'e', 
2936                        'T', 'e', 'x', 't', 0}; 
2937   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
2938                        'S', 'o', 'm', 'e', 
2939                        'O', 't', 'h', 'e', 'r',
2940                        'T', 'e', 'x', 't', 0}; 
2941   const char* streamText = "hello world";
2942   CHARFORMAT2 cf2;
2943   PARAFORMAT2 pf2;
2944   EDITSTREAM es;
2945   
2946   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2947     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2948     FF_DONTCARE, "Courier");
2949   
2950   setText.codepage = 1200;  /* no constant for unicode */
2951   setText.flags = ST_KEEPUNDO;
2952   
2953 
2954   /* modify flag shouldn't be set when richedit is first created */
2955   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2956   ok (result == 0, 
2957       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2958   
2959   /* setting modify flag should actually set it */
2960   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2961   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2962   ok (result != 0, 
2963       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2964   
2965   /* clearing modify flag should actually clear it */
2966   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2967   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2968   ok (result == 0, 
2969       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2970  
2971   /* setting font doesn't change modify flag */
2972   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2973   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2974   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2975   ok (result == 0,
2976       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2977 
2978   /* setting text should set modify flag */
2979   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2980   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2981   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982   ok (result != 0,
2983       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2984   
2985   /* undo previous text doesn't reset modify flag */
2986   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2987   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2988   ok (result != 0,
2989       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2990   
2991   /* set text with no flag to keep undo stack should not set modify flag */
2992   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2993   setText.flags = 0;
2994   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2995   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2996   ok (result == 0,
2997       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2998   
2999   /* WM_SETTEXT doesn't modify */
3000   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3001   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3002   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3003   ok (result == 0,
3004       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3005   
3006   /* clear the text */
3007   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3008   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3009   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3010   ok (result == 0,
3011       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3012