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