richedit: Added tests reveal a problem with EM_SETCHARFORMAT's affect on the undo...
[wine] / dlls / riched20 / tests / editor.c
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);
3014   ok (result != 0,
3015       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3016   
3017   /* copy/paste text 2 */
3018   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3019   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3020   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3021   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3022   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3023   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3024   ok (result != 0,
3025       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3026   
3027   /* press char */
3028   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3029   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3030   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3031   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3032   ok (result != 0,
3033       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3034
3035   /* press del */
3036   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3037   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3038   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3039   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3040   ok (result != 0,
3041       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3042   
3043   /* set char format */
3044   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3045   cf2.cbSize = sizeof(CHARFORMAT2);
3046   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3047              (LPARAM) &cf2);
3048   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3049   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3050   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3051   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3052   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3053   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3054   ok (result != 0,
3055       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3056   
3057   /* set para format */
3058   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3059   pf2.cbSize = sizeof(PARAFORMAT2);
3060   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3061              (LPARAM) &pf2);
3062   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3063   pf2.wAlignment = PFA_RIGHT;
3064   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3065   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3066   ok (result == 0,
3067       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3068
3069   /* EM_STREAM */
3070   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3071   es.dwCookie = (DWORD_PTR)&streamText;
3072   es.dwError = 0;
3073   es.pfnCallback = test_EM_GETMODIFY_esCallback;
3074   SendMessage(hwndRichEdit, EM_STREAMIN, 
3075               (WPARAM)(SF_TEXT), (LPARAM)&es);
3076   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3077   ok (result != 0,
3078       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3079
3080   DestroyWindow(hwndRichEdit);
3081 }
3082
3083 struct exsetsel_s {
3084   long min;
3085   long max;
3086   long expected_retval;
3087   int expected_getsel_start;
3088   int expected_getsel_end;
3089   int _exsetsel_todo_wine;
3090   int _getsel_todo_wine;
3091 };
3092
3093 const struct exsetsel_s exsetsel_tests[] = {
3094   /* sanity tests */
3095   {5, 10, 10, 5, 10, 0, 0},
3096   {15, 17, 17, 15, 17, 0, 0},
3097   /* test cpMax > strlen() */
3098   {0, 100, 18, 0, 18, 0, 1},
3099   /* test cpMin == cpMax */
3100   {5, 5, 5, 5, 5, 0, 0},
3101   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3102   {-1, 0, 5, 5, 5, 0, 0},
3103   {-1, 17, 5, 5, 5, 0, 0},
3104   {-1, 18, 5, 5, 5, 0, 0},
3105   /* test cpMin < 0 && cpMax < 0 */
3106   {-1, -1, 17, 17, 17, 0, 0},
3107   {-4, -5, 17, 17, 17, 0, 0},
3108   /* test cMin >=0 && cpMax < 0 (bug 6814) */
3109   {0, -1, 18, 0, 18, 0, 1},
3110   {17, -5, 18, 17, 18, 0, 1},
3111   {18, -3, 17, 17, 17, 0, 0},
3112   /* test if cpMin > cpMax */
3113   {15, 19, 18, 15, 18, 0, 1},
3114   {19, 15, 18, 15, 18, 0, 1}
3115 };
3116
3117 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3118     CHARRANGE cr;
3119     long result;
3120     int start, end;
3121
3122     cr.cpMin = setsel->min;
3123     cr.cpMax = setsel->max;
3124     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3125
3126     if (setsel->_exsetsel_todo_wine) {
3127         todo_wine {
3128             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3129         }
3130     } else {
3131         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3132     }
3133
3134     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3135
3136     if (setsel->_getsel_todo_wine) {
3137         todo_wine {
3138             ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
3139         }
3140     } else {
3141         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
3142     }
3143 }
3144
3145 static void test_EM_EXSETSEL(void)
3146 {
3147     HWND hwndRichEdit = new_richedit(NULL);
3148     int i;
3149     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3150
3151     /* sending some text to the window */
3152     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3153     /*                                                 01234567890123456*/
3154     /*                                                          10      */
3155
3156     for (i = 0; i < num_tests; i++) {
3157         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3158     }
3159
3160     DestroyWindow(hwndRichEdit);
3161 }
3162
3163 static void test_EM_REPLACESEL(int redraw)
3164 {
3165     HWND hwndRichEdit = new_richedit(NULL);
3166     char buffer[1024] = {0};
3167     int r;
3168     GETTEXTEX getText;
3169     CHARRANGE cr;
3170
3171     /* sending some text to the window */
3172     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3173     /*                                                 01234567890123456*/
3174     /*                                                          10      */
3175
3176     /* FIXME add more tests */
3177     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3178     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3179     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3180     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3181     r = strcmp(buffer, "testing");
3182     ok(0 == r, "expected %d, got %d\n", 0, r);
3183
3184     DestroyWindow(hwndRichEdit);
3185
3186     hwndRichEdit = new_richedit(NULL);
3187
3188     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3189     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3190
3191     /* Test behavior with carriage returns and newlines */
3192     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3193     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3194     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3195     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3196     r = strcmp(buffer, "RichEdit1");
3197     ok(0 == r, "expected %d, got %d\n", 0, r);
3198     getText.cb = 1024;
3199     getText.codepage = CP_ACP;
3200     getText.flags = GT_DEFAULT;
3201     getText.lpDefaultChar = NULL;
3202     getText.lpUsedDefChar = NULL;
3203     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3204     ok(strcmp(buffer, "RichEdit1") == 0,
3205       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3206
3207     /* Test number of lines reported after EM_REPLACESEL */
3208     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3209     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3210
3211     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3212     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3213     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3214     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3215     r = strcmp(buffer, "RichEdit1\r\n");
3216     ok(0 == r, "expected %d, got %d\n", 0, r);
3217     getText.cb = 1024;
3218     getText.codepage = CP_ACP;
3219     getText.flags = GT_DEFAULT;
3220     getText.lpDefaultChar = NULL;
3221     getText.lpUsedDefChar = NULL;
3222     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3223     ok(strcmp(buffer, "RichEdit1\r") == 0,
3224       "EM_GETTEXTEX returned incorrect string\n");
3225
3226     /* Test number of lines reported after EM_REPLACESEL */
3227     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3228     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3229
3230     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3231        EM_REPLACESEL. The general rule seems to be that Win98's riched20
3232        returns the number of characters *inserted* into the control (after
3233        required conversions), but WinXP's riched20 returns the number of
3234        characters interpreted from the original lParam. Wine's builtin riched20
3235        implements the WinXP behavior.
3236      */
3237     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3238     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3239     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3240         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3241
3242     /* Test number of lines reported after EM_REPLACESEL */
3243     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3244     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3245
3246     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3247     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3248     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3249     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3250
3251     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3252     r = strcmp(buffer, "RichEdit1\r\n");
3253     ok(0 == r, "expected %d, got %d\n", 0, r);
3254     getText.cb = 1024;
3255     getText.codepage = CP_ACP;
3256     getText.flags = GT_DEFAULT;
3257     getText.lpDefaultChar = NULL;
3258     getText.lpUsedDefChar = NULL;
3259     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3260     ok(strcmp(buffer, "RichEdit1\r") == 0,
3261       "EM_GETTEXTEX returned incorrect string\n");
3262
3263     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3264     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3265     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3266     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3267
3268     /* The following tests show that richedit should handle the special \r\r\n
3269        sequence by turning it into a single space on insertion. However,
3270        EM_REPLACESEL on WinXP returns the number of characters in the original
3271        string.
3272      */
3273
3274     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3275     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3276     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3277     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3278     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3279     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3280     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3281
3282     /* Test the actual string */
3283     getText.cb = 1024;
3284     getText.codepage = CP_ACP;
3285     getText.flags = GT_DEFAULT;
3286     getText.lpDefaultChar = NULL;
3287     getText.lpUsedDefChar = NULL;
3288     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3289     ok(strcmp(buffer, "\r\r") == 0,
3290       "EM_GETTEXTEX returned incorrect string\n");
3291
3292     /* Test number of lines reported after EM_REPLACESEL */
3293     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3294     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3295
3296     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3297     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3298     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3299         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3300     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3301     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3302     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3303     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3304
3305     /* Test the actual string */
3306     getText.cb = 1024;
3307     getText.codepage = CP_ACP;
3308     getText.flags = GT_DEFAULT;
3309     getText.lpDefaultChar = NULL;
3310     getText.lpUsedDefChar = NULL;
3311     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3312     ok(strcmp(buffer, " ") == 0,
3313       "EM_GETTEXTEX returned incorrect string\n");
3314
3315     /* Test number of lines reported after EM_REPLACESEL */
3316     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3317     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3318
3319     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3320     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3321     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3322         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3323     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3324     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3325     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3326     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3327
3328     /* Test the actual string */
3329     getText.cb = 1024;
3330     getText.codepage = CP_ACP;
3331     getText.flags = GT_DEFAULT;
3332     getText.lpDefaultChar = NULL;
3333     getText.lpUsedDefChar = NULL;
3334     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3335     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3336       "EM_GETTEXTEX returned incorrect string\n");
3337
3338     /* Test number of lines reported after EM_REPLACESEL */
3339     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3340     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3341
3342     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3343     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3344     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3345         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3346     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3347     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3348     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3349     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3350
3351     /* Test the actual string */
3352     getText.cb = 1024;
3353     getText.codepage = CP_ACP;
3354     getText.flags = GT_DEFAULT;
3355     getText.lpDefaultChar = NULL;
3356     getText.lpUsedDefChar = NULL;
3357     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3358     ok(strcmp(buffer, " \r") == 0,
3359       "EM_GETTEXTEX returned incorrect string\n");
3360
3361     /* Test number of lines reported after EM_REPLACESEL */
3362     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3363     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3364
3365     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3366     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3367     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3368         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3369     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3370     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3371     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3372     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3373
3374     /* Test the actual string */
3375     getText.cb = 1024;
3376     getText.codepage = CP_ACP;
3377     getText.flags = GT_DEFAULT;
3378     getText.lpDefaultChar = NULL;
3379     getText.lpUsedDefChar = NULL;
3380     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3381     ok(strcmp(buffer, " \r\r") == 0,
3382       "EM_GETTEXTEX returned incorrect string\n");
3383
3384     /* Test number of lines reported after EM_REPLACESEL */
3385     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3386     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3387
3388     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3389     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3390     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3391         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3392     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3393     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3394     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3395     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3396
3397     /* Test the actual string */
3398     getText.cb = 1024;
3399     getText.codepage = CP_ACP;
3400     getText.flags = GT_DEFAULT;
3401     getText.lpDefaultChar = NULL;
3402     getText.lpUsedDefChar = NULL;
3403     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3404     ok(strcmp(buffer, "\rX\r\r\r") == 0,
3405       "EM_GETTEXTEX returned incorrect string\n");
3406
3407     /* Test number of lines reported after EM_REPLACESEL */
3408     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3409     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3410
3411     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3412     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3413     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3414     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3415     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3416     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3417     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3418
3419     /* Test the actual string */
3420     getText.cb = 1024;
3421     getText.codepage = CP_ACP;
3422     getText.flags = GT_DEFAULT;
3423     getText.lpDefaultChar = NULL;
3424     getText.lpUsedDefChar = NULL;
3425     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3426     ok(strcmp(buffer, "\r\r") == 0,
3427       "EM_GETTEXTEX returned incorrect string\n");
3428
3429     /* Test number of lines reported after EM_REPLACESEL */
3430     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3431     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3432
3433     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3434     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3435     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3436         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3437     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3438     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3439     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3440     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3441
3442     /* Test the actual string */
3443     getText.cb = 1024;
3444     getText.codepage = CP_ACP;
3445     getText.flags = GT_DEFAULT;
3446     getText.lpDefaultChar = NULL;
3447     getText.lpUsedDefChar = NULL;
3448     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3449     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3450       "EM_GETTEXTEX returned incorrect string\n");
3451
3452     /* Test number of lines reported after EM_REPLACESEL */
3453     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3454     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3455
3456     DestroyWindow(hwndRichEdit);
3457 }
3458
3459 static void test_WM_PASTE(void)
3460 {
3461     MSG msg;
3462     int result;
3463     char buffer[1024] = {0};
3464     char key_info[][3] =
3465     {
3466         /* VirtualKey, ScanCode, WM_CHAR code */
3467         {'C', 0x2e,  3},        /* Ctrl-C */
3468         {'X', 0x2d, 24},        /* Ctrl-X */
3469         {'V', 0x2f, 22},        /* Ctrl-V */
3470         {'Z', 0x2c, 26},        /* Ctrl-Z */
3471         {'Y', 0x15, 25},        /* Ctrl-Y */
3472     };
3473     const char* text1 = "testing paste\r";
3474     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3475     const char* text1_after = "testing paste\r\n";
3476     const char* text2 = "testing paste\r\rtesting paste";
3477     const char* text2_after = "testing paste\r\n\r\ntesting paste";
3478     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3479     HWND hwndRichEdit = new_richedit(NULL);
3480
3481     /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3482        messages, probably because it inspects the keyboard state itself.
3483        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3484      */
3485 #define SEND_CTRL_KEY(hwnd, k) \
3486     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
3487     keybd_event(k[0], k[1], 0, 0);\
3488     keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
3489     keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
3490     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
3491         TranslateMessage(&msg); \
3492         DispatchMessage(&msg); \
3493     }
3494
3495 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
3496 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
3497 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
3498 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
3499 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
3500
3501     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3502     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3503
3504     SEND_CTRL_C(hwndRichEdit)   /* Copy */
3505     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3506     SEND_CTRL_V(hwndRichEdit)   /* Paste */
3507     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3508     /* Pasted text should be visible at this step */
3509     result = strcmp(text1_step1, buffer);
3510     ok(result == 0,
3511         "test paste: strcmp = %i\n", result);
3512     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
3513     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3514     /* Text should be the same as before (except for \r -> \r\n conversion) */
3515     result = strcmp(text1_after, buffer);
3516     ok(result == 0,
3517         "test paste: strcmp = %i\n", result);
3518
3519     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3520     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3521     SEND_CTRL_C(hwndRichEdit)   /* Copy */
3522     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3523     SEND_CTRL_V(hwndRichEdit)   /* Paste */
3524     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3525     /* Pasted text should be visible at this step */
3526     result = strcmp(text3, buffer);
3527     ok(result == 0,
3528         "test paste: strcmp = %i\n", result);
3529     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
3530     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3531     /* Text should be the same as before (except for \r -> \r\n conversion) */
3532     result = strcmp(text2_after, buffer);
3533     ok(result == 0,
3534         "test paste: strcmp = %i\n", result);
3535     SEND_CTRL_Y(hwndRichEdit)   /* Redo */
3536     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3537     /* Text should revert to post-paste state */
3538     result = strcmp(buffer,text3);
3539     ok(result == 0,
3540         "test paste: strcmp = %i\n", result);
3541
3542     DestroyWindow(hwndRichEdit);
3543 }
3544
3545 static void test_EM_FORMATRANGE(void)
3546 {
3547   int r;
3548   FORMATRANGE fr;
3549   HDC hdc;
3550   HWND hwndRichEdit = new_richedit(NULL);
3551
3552   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3553
3554   hdc = GetDC(hwndRichEdit);
3555   ok(hdc != NULL, "Could not get HDC\n");
3556
3557   fr.hdc = fr.hdcTarget = hdc;
3558   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3559   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3560   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3561   fr.chrg.cpMin = 0;
3562   fr.chrg.cpMax = 20;
3563
3564   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3565   todo_wine {
3566     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3567   }
3568
3569   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3570   todo_wine {
3571     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3572   }
3573
3574   fr.chrg.cpMin = 0;
3575   fr.chrg.cpMax = 10;
3576
3577   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3578   todo_wine {
3579     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3580   }
3581
3582   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3583   todo_wine {
3584     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3585   }
3586
3587   DestroyWindow(hwndRichEdit);
3588 }
3589
3590 static int nCallbackCount = 0;
3591
3592 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3593                                  LONG cb, LONG* pcb)
3594 {
3595   const char text[] = {'t','e','s','t'};
3596
3597   if (sizeof(text) <= cb)
3598   {
3599     if ((int)dwCookie != nCallbackCount)
3600     {
3601       *pcb = 0;
3602       return 0;
3603     }
3604
3605     memcpy (pbBuff, text, sizeof(text));
3606     *pcb = sizeof(text);
3607
3608     nCallbackCount++;
3609
3610     return 0;
3611   }
3612   else
3613     return 1; /* indicates callback failed */
3614 }
3615
3616 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3617                                          LPBYTE pbBuff,
3618                                          LONG cb,
3619                                          LONG *pcb)
3620 {
3621   const char** str = (const char**)dwCookie;
3622   int size = strlen(*str);
3623   *pcb = cb;
3624   if (*pcb > size) {
3625     *pcb = size;
3626   }
3627   if (*pcb > 0) {
3628     memcpy(pbBuff, *str, *pcb);
3629     *str += *pcb;
3630   }
3631   return 0;
3632 }
3633
3634 struct StringWithLength {
3635     int length;
3636     char *buffer;
3637 };
3638
3639 /* This callback is used to handled the null characters in a string. */
3640 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
3641                                                    LPBYTE pbBuff,
3642                                                    LONG cb,
3643                                                    LONG *pcb)
3644 {
3645     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
3646     int size = str->length;
3647     *pcb = cb;
3648     if (*pcb > size) {
3649       *pcb = size;
3650     }
3651     if (*pcb > 0) {
3652       memcpy(pbBuff, str->buffer, *pcb);
3653       str->buffer += *pcb;
3654       str->length -= *pcb;
3655     }
3656     return 0;
3657 }
3658
3659 static void test_EM_STREAMIN(void)
3660 {
3661   HWND hwndRichEdit = new_richedit(NULL);
3662   LRESULT result;
3663   EDITSTREAM es;
3664   char buffer[1024] = {0};
3665
3666   const char * streamText0 = "{\\rtf1 TestSomeText}";
3667   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3668   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3669
3670   const char * streamText1 =
3671   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3672   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3673   "}\r\n";
3674
3675   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3676   const char * streamText2 =
3677     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3678     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3679     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3680     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3681     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3682     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3683     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3684
3685   const char * streamText3 = "RichEdit1";
3686
3687   struct StringWithLength cookieForStream4;
3688   const char * streamText4 =
3689       "This text just needs to be long enough to cause run to be split onto "\
3690       "two seperate lines and make sure the null terminating character is "\
3691       "handled properly.\0";
3692   int length4 = strlen(streamText4) + 1;
3693   cookieForStream4.buffer = (char *)streamText4;
3694   cookieForStream4.length = length4;
3695
3696   /* Minimal test without \par at the end */
3697   es.dwCookie = (DWORD_PTR)&streamText0;
3698   es.dwError = 0;
3699   es.pfnCallback = test_EM_STREAMIN_esCallback;
3700   SendMessage(hwndRichEdit, EM_STREAMIN,
3701               (WPARAM)(SF_RTF), (LPARAM)&es);
3702
3703   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3704   ok (result  == 12,
3705       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3706   result = strcmp (buffer,"TestSomeText");
3707   ok (result  == 0,
3708       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3709   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3710
3711   /* Native richedit 2.0 ignores last \par */
3712   es.dwCookie = (DWORD_PTR)&streamText0a;
3713   es.dwError = 0;
3714   es.pfnCallback = test_EM_STREAMIN_esCallback;
3715   SendMessage(hwndRichEdit, EM_STREAMIN,
3716               (WPARAM)(SF_RTF), (LPARAM)&es);
3717
3718   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3719   ok (result  == 12,
3720       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3721   result = strcmp (buffer,"TestSomeText");
3722   ok (result  == 0,
3723       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3724   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3725
3726   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3727   es.dwCookie = (DWORD_PTR)&streamText0b;
3728   es.dwError = 0;
3729   es.pfnCallback = test_EM_STREAMIN_esCallback;
3730   SendMessage(hwndRichEdit, EM_STREAMIN,
3731               (WPARAM)(SF_RTF), (LPARAM)&es);
3732
3733   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3734   ok (result  == 14,
3735       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3736   result = strcmp (buffer,"TestSomeText\r\n");
3737   ok (result  == 0,
3738       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3739   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3740
3741   es.dwCookie = (DWORD_PTR)&streamText1;
3742   es.dwError = 0;
3743   es.pfnCallback = test_EM_STREAMIN_esCallback;
3744   SendMessage(hwndRichEdit, EM_STREAMIN,
3745               (WPARAM)(SF_RTF), (LPARAM)&es);
3746
3747   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3748   ok (result  == 12,
3749       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3750   result = strcmp (buffer,"TestSomeText");
3751   ok (result  == 0,
3752       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3753   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3754
3755   es.dwCookie = (DWORD_PTR)&streamText2;
3756   es.dwError = 0;
3757   SendMessage(hwndRichEdit, EM_STREAMIN,
3758               (WPARAM)(SF_RTF), (LPARAM)&es);
3759
3760   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3761   ok (result  == 0,
3762       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3763   ok (strlen(buffer)  == 0,
3764       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3765   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3766
3767   es.dwCookie = (DWORD_PTR)&streamText3;
3768   es.dwError = 0;
3769   SendMessage(hwndRichEdit, EM_STREAMIN,
3770               (WPARAM)(SF_RTF), (LPARAM)&es);
3771
3772   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3773   ok (result  == 0,
3774       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3775   ok (strlen(buffer)  == 0,
3776       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3777   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3778
3779   es.dwCookie = (DWORD_PTR)&cookieForStream4;
3780   es.dwError = 0;
3781   es.pfnCallback = test_EM_STREAMIN_esCallback2;
3782   SendMessage(hwndRichEdit, EM_STREAMIN,
3783               (WPARAM)(SF_TEXT), (LPARAM)&es);
3784
3785   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3786   ok (result  == length4,
3787       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
3788   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
3789
3790   DestroyWindow(hwndRichEdit);
3791 }
3792
3793 static void test_EM_StreamIn_Undo(void)
3794 {
3795   /* The purpose of this test is to determine when a EM_StreamIn should be
3796    * undoable. This is important because WM_PASTE currently uses StreamIn and
3797    * pasting should always be undoable but streaming isn't always.
3798    *
3799    * cases to test:
3800    * StreamIn plain text without SFF_SELECTION.
3801    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3802    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3803    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3804    * Feel free to add tests for other text modes or StreamIn things.
3805    */
3806
3807
3808   HWND hwndRichEdit = new_richedit(NULL);
3809   LRESULT result;
3810   EDITSTREAM es;
3811   char buffer[1024] = {0};
3812   const char randomtext[] = "Some text";
3813
3814   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3815
3816   /* StreamIn, no SFF_SELECTION */
3817   es.dwCookie = nCallbackCount;
3818   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3819   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3820   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3821   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3822   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3823   result = strcmp (buffer,"test");
3824   ok (result  == 0,
3825       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3826
3827   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3828   ok (result == FALSE,
3829       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3830
3831   /* StreamIn, SFF_SELECTION, but nothing selected */
3832   es.dwCookie = nCallbackCount;
3833   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3834   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3835   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3836   SendMessage(hwndRichEdit, EM_STREAMIN,
3837               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3838   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3839   result = strcmp (buffer,"testSome text");
3840   ok (result  == 0,
3841       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3842
3843   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3844   ok (result == TRUE,
3845      "EM_STREAMIN with SFF_SELECTION but no selection set "
3846       "should create an undo\n");
3847
3848   /* StreamIn, SFF_SELECTION, with a selection */
3849   es.dwCookie = nCallbackCount;
3850   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3851   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3852   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3853   SendMessage(hwndRichEdit, EM_STREAMIN,
3854               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3855   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3856   result = strcmp (buffer,"Sometesttext");
3857   ok (result  == 0,
3858       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3859
3860   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3861   ok (result == TRUE,
3862       "EM_STREAMIN with SFF_SELECTION and selection set "
3863       "should create an undo\n");
3864
3865 }
3866
3867 static BOOL is_em_settextex_supported(HWND hwnd)
3868 {
3869     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3870     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3871 }
3872
3873 static void test_unicode_conversions(void)
3874 {
3875     static const WCHAR tW[] = {'t',0};
3876     static const WCHAR teW[] = {'t','e',0};
3877     static const WCHAR textW[] = {'t','e','s','t',0};
3878     static const char textA[] = "test";
3879     char bufA[64];
3880     WCHAR bufW[64];
3881     HWND hwnd;
3882     int is_win9x, em_settextex_supported, ret;
3883
3884     is_win9x = GetVersion() & 0x80000000;
3885
3886 #define set_textA(hwnd, wm_set_text, txt) \
3887     do { \
3888         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3889         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3890         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3891         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3892         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3893     } while(0)
3894 #define expect_textA(hwnd, wm_get_text, txt) \
3895     do { \
3896         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3897         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3898         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3899         memset(bufA, 0xAA, sizeof(bufA)); \
3900         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3901         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3902         ret = lstrcmpA(bufA, txt); \
3903         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3904     } while(0)
3905
3906 #define set_textW(hwnd, wm_set_text, txt) \
3907     do { \
3908         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3909         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3910         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3911         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3912         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3913     } while(0)
3914 #define expect_textW(hwnd, wm_get_text, txt) \
3915     do { \
3916         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3917         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3918         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3919         memset(bufW, 0xAA, sizeof(bufW)); \
3920         if (is_win9x) \
3921         { \
3922             assert(wm_get_text == EM_GETTEXTEX); \
3923             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3924             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3925         } \
3926         else \
3927         { \
3928             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3929             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3930         } \
3931         ret = lstrcmpW(bufW, txt); \
3932         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3933     } while(0)
3934 #define expect_empty(hwnd, wm_get_text) \
3935     do { \
3936         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3937         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3938         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3939         memset(bufA, 0xAA, sizeof(bufA)); \
3940         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3941         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3942         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3943     } while(0)
3944
3945     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3946                            0, 0, 200, 60, 0, 0, 0, 0);
3947     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3948
3949     ret = IsWindowUnicode(hwnd);
3950     if (is_win9x)
3951         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3952     else
3953         ok(ret, "RichEdit20W should be unicode under NT\n");
3954
3955     /* EM_SETTEXTEX is supported starting from version 3.0 */
3956     em_settextex_supported = is_em_settextex_supported(hwnd);
3957     trace("EM_SETTEXTEX is %ssupported on this platform\n",
3958           em_settextex_supported ? "" : "NOT ");
3959
3960     expect_empty(hwnd, WM_GETTEXT);
3961     expect_empty(hwnd, EM_GETTEXTEX);
3962
3963     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3964     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3965     expect_textA(hwnd, WM_GETTEXT, "t");
3966     expect_textA(hwnd, EM_GETTEXTEX, "t");
3967     expect_textW(hwnd, EM_GETTEXTEX, tW);
3968
3969     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3970     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3971     expect_textA(hwnd, WM_GETTEXT, "te");
3972     expect_textA(hwnd, EM_GETTEXTEX, "te");
3973     expect_textW(hwnd, EM_GETTEXTEX, teW);
3974
3975     set_textA(hwnd, WM_SETTEXT, NULL);
3976     expect_empty(hwnd, WM_GETTEXT);
3977     expect_empty(hwnd, EM_GETTEXTEX);
3978
3979     if (is_win9x)
3980         set_textA(hwnd, WM_SETTEXT, textW);
3981     else
3982         set_textA(hwnd, WM_SETTEXT, textA);
3983     expect_textA(hwnd, WM_GETTEXT, textA);
3984     expect_textA(hwnd, EM_GETTEXTEX, textA);
3985     expect_textW(hwnd, EM_GETTEXTEX, textW);
3986
3987     if (em_settextex_supported)
3988     {
3989         set_textA(hwnd, EM_SETTEXTEX, textA);
3990         expect_textA(hwnd, WM_GETTEXT, textA);
3991         expect_textA(hwnd, EM_GETTEXTEX, textA);
3992         expect_textW(hwnd, EM_GETTEXTEX, textW);
3993     }
3994
3995     if (!is_win9x)
3996     {
3997         set_textW(hwnd, WM_SETTEXT, textW);
3998         expect_textW(hwnd, WM_GETTEXT, textW);
3999         expect_textA(hwnd, WM_GETTEXT, textA);
4000         expect_textW(hwnd, EM_GETTEXTEX, textW);
4001         expect_textA(hwnd, EM_GETTEXTEX, textA);
4002
4003         if (em_settextex_supported)
4004         {
4005             set_textW(hwnd, EM_SETTEXTEX, textW);
4006             expect_textW(hwnd, WM_GETTEXT, textW);
4007             expect_textA(hwnd, WM_GETTEXT, textA);
4008             expect_textW(hwnd, EM_GETTEXTEX, textW);
4009             expect_textA(hwnd, EM_GETTEXTEX, textA);
4010         }
4011     }
4012     DestroyWindow(hwnd);
4013
4014     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4015                            0, 0, 200, 60, 0, 0, 0, 0);
4016     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4017
4018     ret = IsWindowUnicode(hwnd);
4019     ok(!ret, "RichEdit20A should NOT be unicode\n");
4020
4021     set_textA(hwnd, WM_SETTEXT, textA);
4022     expect_textA(hwnd, WM_GETTEXT, textA);
4023     expect_textA(hwnd, EM_GETTEXTEX, textA);
4024     expect_textW(hwnd, EM_GETTEXTEX, textW);
4025
4026     if (em_settextex_supported)
4027     {
4028         set_textA(hwnd, EM_SETTEXTEX, textA);
4029         expect_textA(hwnd, WM_GETTEXT, textA);
4030         expect_textA(hwnd, EM_GETTEXTEX, textA);
4031         expect_textW(hwnd, EM_GETTEXTEX, textW);
4032     }
4033
4034     if (!is_win9x)
4035     {
4036         set_textW(hwnd, WM_SETTEXT, textW);
4037         expect_textW(hwnd, WM_GETTEXT, textW);
4038         expect_textA(hwnd, WM_GETTEXT, textA);
4039         expect_textW(hwnd, EM_GETTEXTEX, textW);
4040         expect_textA(hwnd, EM_GETTEXTEX, textA);
4041
4042         if (em_settextex_supported)
4043         {
4044             set_textW(hwnd, EM_SETTEXTEX, textW);
4045             expect_textW(hwnd, WM_GETTEXT, textW);
4046             expect_textA(hwnd, WM_GETTEXT, textA);
4047             expect_textW(hwnd, EM_GETTEXTEX, textW);
4048             expect_textA(hwnd, EM_GETTEXTEX, textA);
4049         }
4050     }
4051     DestroyWindow(hwnd);
4052 }
4053
4054 static void test_WM_CHAR(void)
4055 {
4056     HWND hwnd;
4057     int ret;
4058     const char * char_list = "abc\rabc\r";
4059     const char * expected_content_single = "abcabc";
4060     const char * expected_content_multi = "abc\r\nabc\r\n";
4061     char buffer[64] = {0};
4062     const char * p;
4063
4064     /* single-line control must IGNORE carriage returns */
4065     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4066                            0, 0, 200, 60, 0, 0, 0, 0);
4067     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4068
4069     p = char_list;
4070     while (*p != '\0') {
4071         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4072         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4073         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4074         SendMessageA(hwnd, WM_KEYUP, *p, 1);
4075         p++;
4076     }
4077
4078     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4079     ret = strcmp(buffer, expected_content_single);
4080     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4081
4082     DestroyWindow(hwnd);
4083
4084     /* multi-line control inserts CR normally */
4085     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4086                            0, 0, 200, 60, 0, 0, 0, 0);
4087     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4088
4089     p = char_list;
4090     while (*p != '\0') {
4091         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4092         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4093         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4094         SendMessageA(hwnd, WM_KEYUP, *p, 1);
4095         p++;
4096     }
4097
4098     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4099     ret = strcmp(buffer, expected_content_multi);
4100     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4101
4102     DestroyWindow(hwnd);
4103 }
4104
4105 static void test_EM_GETTEXTLENGTHEX(void)
4106 {
4107     HWND hwnd;
4108     GETTEXTLENGTHEX gtl;
4109     int ret;
4110     const char * base_string = "base string";
4111     const char * test_string = "a\nb\n\n\r\n";
4112     const char * test_string_after = "a";
4113     const char * test_string_2 = "a\rtest\rstring";
4114     char buffer[64] = {0};
4115
4116     /* single line */
4117     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4118                            0, 0, 200, 60, 0, 0, 0, 0);
4119     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4120
4121     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4122     gtl.codepage = CP_ACP;
4123     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4124     ok(ret == 0, "ret %d\n",ret);
4125
4126     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4127     gtl.codepage = CP_ACP;
4128     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4129     ok(ret == 0, "ret %d\n",ret);
4130
4131     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4132
4133     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4134     gtl.codepage = CP_ACP;
4135     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4136     ok(ret == strlen(base_string), "ret %d\n",ret);
4137
4138     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4139     gtl.codepage = CP_ACP;
4140     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4141     ok(ret == strlen(base_string), "ret %d\n",ret);
4142
4143     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4144
4145     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4146     gtl.codepage = CP_ACP;
4147     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4148     ok(ret == 1, "ret %d\n",ret);
4149
4150     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4151     gtl.codepage = CP_ACP;
4152     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4153     ok(ret == 1, "ret %d\n",ret);
4154
4155     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4156     ret = strcmp(buffer, test_string_after);
4157     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4158
4159     DestroyWindow(hwnd);
4160
4161     /* multi line */
4162     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4163                            0, 0, 200, 60, 0, 0, 0, 0);
4164     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4165
4166     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4167     gtl.codepage = CP_ACP;
4168     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4169     ok(ret == 0, "ret %d\n",ret);
4170
4171     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4172     gtl.codepage = CP_ACP;
4173     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4174     ok(ret == 0, "ret %d\n",ret);
4175
4176     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4177
4178     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4179     gtl.codepage = CP_ACP;
4180     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4181     ok(ret == strlen(base_string), "ret %d\n",ret);
4182
4183     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4184     gtl.codepage = CP_ACP;
4185     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4186     ok(ret == strlen(base_string), "ret %d\n",ret);
4187
4188     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4189
4190     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4191     gtl.codepage = CP_ACP;
4192     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4193     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4194
4195     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4196     gtl.codepage = CP_ACP;
4197     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4198     ok(ret == strlen(test_string_2), "ret %d\n",ret);
4199
4200     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4201
4202     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4203     gtl.codepage = CP_ACP;
4204     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4205     ok(ret == 10, "ret %d\n",ret);
4206
4207     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4208     gtl.codepage = CP_ACP;
4209     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4210     ok(ret == 6, "ret %d\n",ret);
4211
4212     DestroyWindow(hwnd);
4213 }
4214
4215
4216 /* globals that parent and child access when checking event masks & notifications */
4217 static HWND eventMaskEditHwnd = 0;
4218 static int queriedEventMask;
4219 static int watchForEventMask = 0;
4220
4221 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4222 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4223 {
4224     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4225     {
4226       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4227     }
4228     return DefWindowProcA(hwnd, message, wParam, lParam);
4229 }
4230
4231 /* test event masks in combination with WM_COMMAND */
4232 static void test_eventMask(void)
4233 {
4234     HWND parent;
4235     int ret;
4236     WNDCLASSA cls;
4237     const char text[] = "foo bar\n";
4238     int eventMask;
4239
4240     /* register class to capture WM_COMMAND */
4241     cls.style = 0;
4242     cls.lpfnWndProc = ParentMsgCheckProcA;
4243     cls.cbClsExtra = 0;
4244     cls.cbWndExtra = 0;
4245     cls.hInstance = GetModuleHandleA(0);
4246     cls.hIcon = 0;
4247     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4248     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4249     cls.lpszMenuName = NULL;
4250     cls.lpszClassName = "EventMaskParentClass";
4251     if(!RegisterClassA(&cls)) assert(0);
4252
4253     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4254                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
4255     ok (parent != 0, "Failed to create parent window\n");
4256
4257     eventMaskEditHwnd = new_richedit(parent);
4258     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4259
4260     eventMask = ENM_CHANGE | ENM_UPDATE;
4261     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4262     ok(ret == ENM_NONE, "wrong event mask\n");
4263     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4264     ok(ret == eventMask, "failed to set event mask\n");
4265
4266     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4267     queriedEventMask = 0;  /* initialize to something other than we expect */
4268     watchForEventMask = EN_CHANGE;
4269     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4270     ok(ret == TRUE, "failed to set text\n");
4271     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4272        notification in response to WM_SETTEXT */
4273     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4274             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4275
4276 }
4277
4278 static int received_WM_NOTIFY = 0;
4279 static int modify_at_WM_NOTIFY = 0;
4280 static HWND hwndRichedit_WM_NOTIFY;
4281
4282 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4283 {
4284     if(message == WM_NOTIFY)
4285     {
4286       received_WM_NOTIFY = 1;
4287       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4288     }
4289     return DefWindowProcA(hwnd, message, wParam, lParam);
4290 }
4291
4292 static void test_WM_NOTIFY(void)
4293 {
4294     HWND parent;
4295     WNDCLASSA cls;
4296     CHARFORMAT2 cf2;
4297
4298     /* register class to capture WM_NOTIFY */
4299     cls.style = 0;
4300     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4301     cls.cbClsExtra = 0;
4302     cls.cbWndExtra = 0;
4303     cls.hInstance = GetModuleHandleA(0);
4304     cls.hIcon = 0;
4305     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4306     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4307     cls.lpszMenuName = NULL;
4308     cls.lpszClassName = "WM_NOTIFY_ParentClass";
4309     if(!RegisterClassA(&cls)) assert(0);
4310
4311     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4312                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
4313     ok (parent != 0, "Failed to create parent window\n");
4314
4315     hwndRichedit_WM_NOTIFY = new_richedit(parent);
4316     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4317
4318     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4319
4320     /* Notifications for selection change should only be sent when selection
4321        actually changes. EM_SETCHARFORMAT is one message that calls
4322        ME_CommitUndo, which should check whether message should be sent */
4323     received_WM_NOTIFY = 0;
4324     cf2.cbSize = sizeof(CHARFORMAT2);
4325     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4326              (LPARAM) &cf2);
4327     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4328     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4329     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4330     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4331
4332     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4333        already at 0. */
4334     received_WM_NOTIFY = 0;
4335     modify_at_WM_NOTIFY = 0;
4336     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4337     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4338     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4339
4340     received_WM_NOTIFY = 0;
4341     modify_at_WM_NOTIFY = 0;
4342     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4343     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4344
4345     received_WM_NOTIFY = 0;
4346     modify_at_WM_NOTIFY = 0;
4347     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4348     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4349     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4350
4351     DestroyWindow(hwndRichedit_WM_NOTIFY);
4352     DestroyWindow(parent);
4353 }
4354
4355 static void simulate_typing_characters(HWND hwnd, const char* szChars)
4356 {
4357     int ret;
4358
4359     while (*szChars != '\0') {
4360         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
4361         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
4362         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
4363         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
4364         szChars++;
4365     }
4366 }
4367
4368 static void test_undo_coalescing(void)
4369 {
4370     HWND hwnd;
4371     int result;
4372     char buffer[64] = {0};
4373
4374     /* multi-line control inserts CR normally */
4375     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4376                            0, 0, 200, 60, 0, 0, 0, 0);
4377     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4378
4379     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4380     ok (result == FALSE, "Can undo after window creation.\n");
4381     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4382     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
4383     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4384     ok (result == FALSE, "Can redo after window creation.\n");
4385     result = SendMessage(hwnd, EM_REDO, 0, 0);
4386     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
4387
4388     /* Test the effect of arrows keys during typing on undo transactions*/
4389     simulate_typing_characters(hwnd, "one two three");
4390     SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
4391     SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
4392     simulate_typing_characters(hwnd, " four five six");
4393
4394     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4395     ok (result == FALSE, "Can redo before anything is undone.\n");
4396     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4397     ok (result == TRUE, "Cannot undo typed characters.\n");
4398     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4399     todo_wine ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
4400     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4401     ok (result == TRUE, "Cannot redo after undo.\n");
4402     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4403     result = strcmp(buffer, "one two three");
4404     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4405
4406     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4407     ok (result == TRUE, "Cannot undo typed characters.\n");
4408     result = SendMessage(hwnd, WM_UNDO, 0, 0);
4409     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4410     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4411     result = strcmp(buffer, "");
4412     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4413
4414     /* Test the effect of focus changes during typing on undo transactions*/
4415     simulate_typing_characters(hwnd, "one two three");
4416     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4417     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4418     SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
4419     SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
4420     simulate_typing_characters(hwnd, " four five six");
4421     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4422     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4423     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4424     result = strcmp(buffer, "one two three");
4425     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4426
4427     /* Test the effect of the back key during typing on undo transactions */
4428     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4429     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4430     ok (result == TRUE, "Failed to clear the text.\n");
4431     simulate_typing_characters(hwnd, "one two threa");
4432     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4433     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4434     SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
4435     SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
4436     simulate_typing_characters(hwnd, "e four five six");
4437     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4438     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4439     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4440     result = strcmp(buffer, "");
4441     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4442
4443     /* Test the effect of the delete key during typing on undo transactions */
4444     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4445     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
4446     ok(result == TRUE, "Failed to set the text.\n");
4447     SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
4448     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4449     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4450     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4451     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4452     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4453     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4454     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4455     result = strcmp(buffer, "acd");
4456     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
4457     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4458     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4459     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4460     result = strcmp(buffer, "abcd");
4461     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
4462
4463     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
4464     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4465     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4466     ok (result == TRUE, "Failed to clear the text.\n");
4467     simulate_typing_characters(hwnd, "one two three");
4468     result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
4469     ok (result == 0, "expected %d but got %d\n", 0, result);
4470     simulate_typing_characters(hwnd, " four five six");
4471     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4472     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4473     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4474     result = strcmp(buffer, "one two three");
4475     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4476     result = SendMessage(hwnd, EM_UNDO, 0, 0);
4477     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4478     todo_wine ok (result == TRUE, "Failed to undo typed characters.\n");
4479     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4480     result = strcmp(buffer, "");
4481     todo_wine ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4482
4483     DestroyWindow(hwnd);
4484 }
4485
4486 /* Used to simulate a Ctrl-Arrow Key press. */
4487 #define SEND_CTRL_EXT_KEY(hwnd, vk, scancode) \
4488     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
4489     keybd_event(vk, scancode, KEYEVENTF_EXTENDEDKEY, 0);\
4490     keybd_event(vk, scancode | 0x80, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, 0);\
4491     keybd_event(VK_CONTROL, 0x1d | 0x80, KEYEVENTF_KEYUP, 0); \
4492     while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { \
4493         TranslateMessage(&msg); \
4494         DispatchMessage(&msg); \
4495     }
4496 #define SEND_CTRL_LEFT(hwnd) SEND_CTRL_EXT_KEY(hwnd, VK_LEFT, 0x4b)
4497 #define SEND_CTRL_RIGHT(hwnd) SEND_CTRL_EXT_KEY(hwnd, VK_RIGHT, 0x4d)
4498
4499 static void test_word_movement(){
4500     HWND hwnd;
4501     int result;
4502     int sel_start, sel_end;
4503     MSG msg;
4504
4505     /* multi-line control inserts CR normally */
4506     hwnd = new_richedit(NULL);
4507
4508     SendMessage(hwnd, WM_SETFOCUS, (WPARAM)hwnd, 0);
4509     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
4510     ok (result == TRUE, "Failed to clear the text.\n");
4511     SendMessage(hwnd, EM_SETSEL, 0, 0);
4512     /* |one two three */
4513
4514     SEND_CTRL_RIGHT(hwnd)
4515     /* one |two  three */
4516     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4517     ok(sel_start == sel_end, "Selection should be empty\n");
4518     ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4519
4520     SEND_CTRL_RIGHT(hwnd)
4521     /* one two  |three */
4522     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4523     ok(sel_start == sel_end, "Selection should be empty\n");
4524     ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4525
4526     SEND_CTRL_LEFT(hwnd)
4527     /* one |two  three */
4528     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4529     ok(sel_start == sel_end, "Selection should be empty\n");
4530     ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4531
4532     SEND_CTRL_LEFT(hwnd)
4533     /* |one two  three */
4534     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4535     ok(sel_start == sel_end, "Selection should be empty\n");
4536     ok(sel_start == 0, "Cursur is at %d instead of %d\n", sel_start, 0);
4537
4538     SendMessage(hwnd, EM_SETSEL, 8, 8);
4539     /* one two | three */
4540     SEND_CTRL_RIGHT(hwnd)
4541     /* one two  |three */
4542     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4543     ok(sel_start == sel_end, "Selection should be empty\n");
4544     ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4545
4546     SendMessage(hwnd, EM_SETSEL, 11, 11);
4547     /* one two  th|ree */
4548     SEND_CTRL_LEFT(hwnd)
4549     /* one two  |three */
4550     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4551     ok(sel_start == sel_end, "Selection should be empty\n");
4552     ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4553
4554     DestroyWindow(hwnd);
4555 }
4556
4557 START_TEST( editor )
4558 {
4559   MSG msg;
4560   time_t end;
4561
4562   /* Must explicitly LoadLibrary(). The test has no references to functions in
4563    * RICHED20.DLL, so the linker doesn't actually link to it. */
4564   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4565   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4566   test_WM_CHAR();
4567   test_EM_FINDTEXT();
4568   test_EM_GETLINE();
4569   test_EM_POSFROMCHAR();
4570   test_EM_SCROLLCARET();
4571   test_EM_SCROLL();
4572   test_WM_SETTEXT();
4573   test_EM_LINELENGTH();
4574   test_EM_SETCHARFORMAT();
4575   test_EM_SETTEXTMODE();
4576   test_TM_PLAINTEXT();
4577   test_EM_SETOPTIONS();
4578   test_WM_GETTEXT();
4579   test_EM_GETTEXTRANGE();
4580   test_EM_GETSELTEXT();
4581   test_EM_SETUNDOLIMIT();
4582   test_ES_PASSWORD();
4583   test_EM_SETTEXTEX();
4584   test_EM_LIMITTEXT();
4585   test_EM_EXLIMITTEXT();
4586   test_EM_GETLIMITTEXT();
4587   test_WM_SETFONT();
4588   test_EM_GETMODIFY();
4589   test_EM_EXSETSEL();
4590   test_WM_PASTE();
4591   test_EM_STREAMIN();
4592   test_EM_STREAMOUT();
4593   test_EM_StreamIn_Undo();
4594   test_EM_FORMATRANGE();
4595   test_unicode_conversions();
4596   test_EM_GETTEXTLENGTHEX();
4597   test_EM_REPLACESEL(1);
4598   test_EM_REPLACESEL(0);
4599   test_WM_NOTIFY();
4600   test_EM_AUTOURLDETECT();
4601   test_eventMask();
4602   test_undo_coalescing();
4603   test_word_movement();
4604
4605   /* Set the environment variable WINETEST_RICHED20 to keep windows
4606    * responsive and open for 30 seconds. This is useful for debugging.
4607    *
4608    * The message pump uses PeekMessage() to empty the queue and then sleeps for
4609    * 50ms before retrying the queue. */
4610   end = time(NULL) + 30;
4611   if (getenv( "WINETEST_RICHED20" )) {
4612     while (time(NULL) < end) {
4613       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4614         TranslateMessage(&msg);
4615         DispatchMessage(&msg);
4616       } else {
4617         Sleep(50);
4618       }
4619     }
4620   }
4621
4622   OleFlushClipboard();
4623   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
4624 }