richedit: Add tests for URL autodetection for EM_SETTEXTEX, make them pass under...
[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_SETCHARFORMAT(void)
401 {
402   HWND hwndRichEdit = new_richedit(NULL);
403   CHARFORMAT2 cf2;
404   int rc = 0;
405   int tested_effects[] = {
406     CFE_BOLD,
407     CFE_ITALIC,
408     CFE_UNDERLINE,
409     CFE_STRIKEOUT,
410     CFE_PROTECTED,
411     CFE_LINK,
412     CFE_SUBSCRIPT,
413     CFE_SUPERSCRIPT,
414     0
415   };
416   int i;
417
418   /* Invalid flags, CHARFORMAT2 structure blanked out */
419   memset(&cf2, 0, sizeof(cf2));
420   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
421              (LPARAM) &cf2);
422   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
423
424   /* A valid flag, CHARFORMAT2 structure blanked out */
425   memset(&cf2, 0, sizeof(cf2));
426   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
427              (LPARAM) &cf2);
428   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
429
430   /* A valid flag, CHARFORMAT2 structure blanked out */
431   memset(&cf2, 0, sizeof(cf2));
432   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
433              (LPARAM) &cf2);
434   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
435
436   /* A valid flag, CHARFORMAT2 structure blanked out */
437   memset(&cf2, 0, sizeof(cf2));
438   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
439              (LPARAM) &cf2);
440   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
441
442   /* A valid flag, CHARFORMAT2 structure blanked out */
443   memset(&cf2, 0, sizeof(cf2));
444   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
445              (LPARAM) &cf2);
446   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
447
448   /* Invalid flags, CHARFORMAT2 structure minimally filled */
449   memset(&cf2, 0, sizeof(cf2));
450   cf2.cbSize = sizeof(CHARFORMAT2);
451   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
452              (LPARAM) &cf2);
453   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
454
455   /* A valid flag, CHARFORMAT2 structure minimally filled */
456   memset(&cf2, 0, sizeof(cf2));
457   cf2.cbSize = sizeof(CHARFORMAT2);
458   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
459              (LPARAM) &cf2);
460   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
461
462   /* A valid flag, CHARFORMAT2 structure minimally filled */
463   memset(&cf2, 0, sizeof(cf2));
464   cf2.cbSize = sizeof(CHARFORMAT2);
465   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
466              (LPARAM) &cf2);
467   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
468
469   /* A valid flag, CHARFORMAT2 structure minimally filled */
470   memset(&cf2, 0, sizeof(cf2));
471   cf2.cbSize = sizeof(CHARFORMAT2);
472   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
473              (LPARAM) &cf2);
474   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
475
476   /* A valid flag, CHARFORMAT2 structure minimally filled */
477   memset(&cf2, 0, sizeof(cf2));
478   cf2.cbSize = sizeof(CHARFORMAT2);
479   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
480              (LPARAM) &cf2);
481   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
482
483   cf2.cbSize = sizeof(CHARFORMAT2);
484   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
485              (LPARAM) &cf2);
486
487   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
488   cf2.cbSize = sizeof(CHARFORMAT2);
489   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
490              (LPARAM) &cf2);
491   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
492   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
493
494   /* wParam==0 is default char format, does not set modify */
495   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
496   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
497   ok(rc == 0, "Text marked as modified, expected not modified!\n");
498   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
499   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
500   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
501   ok(rc == 0, "Text marked as modified, expected not modified!\n");
502
503   /* wParam==SCF_SELECTION sets modify if nonempty selection */
504   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
505   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
506   ok(rc == 0, "Text marked as modified, expected not modified!\n");
507   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
508   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
509   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
510   ok(rc == 0, "Text marked as modified, expected not modified!\n");
511
512   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
513   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
514   ok(rc == 0, "Text marked as modified, expected not modified!\n");
515   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
516   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
517   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
518   ok(rc == 0, "Text marked as modified, expected not modified!\n");
519   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
520   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
521   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
522   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
523   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
524
525   /* wParam==SCF_ALL sets modify regardless of whether text is present */
526   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
527   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
528   ok(rc == 0, "Text marked as modified, expected not modified!\n");
529   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
530   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
531   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
532   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
533
534   DestroyWindow(hwndRichEdit);
535
536   /* EM_GETCHARFORMAT tests */
537   for (i = 0; tested_effects[i]; i++)
538   {
539     hwndRichEdit = new_richedit(NULL);
540     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
541
542     memset(&cf2, 0, sizeof(CHARFORMAT2));
543     cf2.cbSize = sizeof(CHARFORMAT2);
544     cf2.dwMask = tested_effects[i];
545     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
546       cf2.dwMask = CFM_SUPERSCRIPT;
547     cf2.dwEffects = tested_effects[i];
548     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
549     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
550
551     memset(&cf2, 0, sizeof(CHARFORMAT2));
552     cf2.cbSize = sizeof(CHARFORMAT2);
553     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
554     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
555     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
556           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
557           ||
558           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
559         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
560     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
561         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
562
563     memset(&cf2, 0, sizeof(CHARFORMAT2));
564     cf2.cbSize = sizeof(CHARFORMAT2);
565     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
566     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
567     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
568           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
569           ||
570           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
571         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
572     ok((cf2.dwEffects & tested_effects[i]) == 0,
573         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
574
575     memset(&cf2, 0, sizeof(CHARFORMAT2));
576     cf2.cbSize = sizeof(CHARFORMAT2);
577     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
578     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
579     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
580           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
581           ||
582           (cf2.dwMask & tested_effects[i]) == 0),
583         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
584     ok((cf2.dwEffects & tested_effects[i]) == 0,
585         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
586
587     DestroyWindow(hwndRichEdit);
588   }
589
590   for (i = 0; tested_effects[i]; i++)
591   {
592     hwndRichEdit = new_richedit(NULL);
593     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
594
595     memset(&cf2, 0, sizeof(CHARFORMAT2));
596     cf2.cbSize = sizeof(CHARFORMAT2);
597     cf2.dwMask = tested_effects[i];
598     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
599       cf2.dwMask = CFM_SUPERSCRIPT;
600     cf2.dwEffects = tested_effects[i];
601     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
602     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
603
604     memset(&cf2, 0, sizeof(CHARFORMAT2));
605     cf2.cbSize = sizeof(CHARFORMAT2);
606     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
607     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
608     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
609           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
610           ||
611           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
612         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
613     ok((cf2.dwEffects & tested_effects[i]) == 0,
614         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
615
616     memset(&cf2, 0, sizeof(CHARFORMAT2));
617     cf2.cbSize = sizeof(CHARFORMAT2);
618     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
619     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
620     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
621           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
622           ||
623           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
624         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
625     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
626         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
627
628     memset(&cf2, 0, sizeof(CHARFORMAT2));
629     cf2.cbSize = sizeof(CHARFORMAT2);
630     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
631     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
632     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
633           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
634           ||
635           (cf2.dwMask & tested_effects[i]) == 0),
636         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
637     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
638         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
639
640     DestroyWindow(hwndRichEdit);
641   }
642 }
643
644 static void test_EM_SETTEXTMODE(void)
645 {
646   HWND hwndRichEdit = new_richedit(NULL);
647   CHARFORMAT2 cf2, cf2test;
648   CHARRANGE cr;
649   int rc = 0;
650
651   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
652   /*Insert text into the control*/
653
654   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
655
656   /*Attempt to change the control to plain text mode*/
657   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
658   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
659
660   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
661   If rich text is pasted, it should have the same formatting as the rest
662   of the text in the control*/
663
664   /*Italicize the text
665   *NOTE: If the default text was already italicized, the test will simply
666   reverse; in other words, it will copy a regular "wine" into a plain
667   text window that uses an italicized format*/
668   cf2.cbSize = sizeof(CHARFORMAT2);
669   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
670              (LPARAM) &cf2);
671
672   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
673   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
674
675   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
676   ok(rc == 0, "Text marked as modified, expected not modified!\n");
677
678   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
679   however, SCF_ALL has been implemented*/
680   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
681   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
682
683   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
684   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
685
686   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
687
688   /*Select the string "wine"*/
689   cr.cpMin = 0;
690   cr.cpMax = 4;
691   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
692
693   /*Copy the italicized "wine" to the clipboard*/
694   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
695
696   /*Reset the formatting to default*/
697   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
698   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
699   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
700
701   /*Clear the text in the control*/
702   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
703
704   /*Switch to Plain Text Mode*/
705   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
706   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
707
708   /*Input "wine" again in normal format*/
709   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
710
711   /*Paste the italicized "wine" into the control*/
712   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
713
714   /*Select a character from the first "wine" string*/
715   cr.cpMin = 2;
716   cr.cpMax = 3;
717   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
718
719   /*Retrieve its formatting*/
720   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
721               (LPARAM) &cf2);
722
723   /*Select a character from the second "wine" string*/
724   cr.cpMin = 5;
725   cr.cpMax = 6;
726   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
727
728   /*Retrieve its formatting*/
729   cf2test.cbSize = sizeof(CHARFORMAT2);
730   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
731                (LPARAM) &cf2test);
732
733   /*Compare the two formattings*/
734     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
735       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
736        cf2.dwEffects, cf2test.dwEffects);
737   /*Test TM_RICHTEXT by: switching back to Rich Text mode
738                          printing "wine" in the current format(normal)
739                          pasting "wine" from the clipboard(italicized)
740                          comparing the two formats(should differ)*/
741
742   /*Attempt to switch with text in control*/
743   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
744   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
745
746   /*Clear control*/
747   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
748
749   /*Switch into Rich Text mode*/
750   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
751   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
752
753   /*Print "wine" in normal formatting into the control*/
754   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
755
756   /*Paste italicized "wine" into the control*/
757   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
758
759   /*Select text from the first "wine" string*/
760   cr.cpMin = 1;
761   cr.cpMax = 3;
762   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
763
764   /*Retrieve its formatting*/
765   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
766                 (LPARAM) &cf2);
767
768   /*Select text from the second "wine" string*/
769   cr.cpMin = 6;
770   cr.cpMax = 7;
771   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
772
773   /*Retrieve its formatting*/
774   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
775                 (LPARAM) &cf2test);
776
777   /*Test that the two formattings are not the same*/
778   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
779       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
780       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
781
782   DestroyWindow(hwndRichEdit);
783 }
784
785 static void test_TM_PLAINTEXT(void)
786 {
787   /*Tests plain text properties*/
788
789   HWND hwndRichEdit = new_richedit(NULL);
790   CHARFORMAT2 cf2, cf2test;
791   CHARRANGE cr;
792   int rc = 0;
793
794   /*Switch to plain text mode*/
795
796   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
797   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
798
799   /*Fill control with text*/
800
801   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
802
803   /*Select some text and bold it*/
804
805   cr.cpMin = 10;
806   cr.cpMax = 20;
807   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
808   cf2.cbSize = sizeof(CHARFORMAT2);
809   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
810               (LPARAM) &cf2);
811
812   cf2.dwMask = CFM_BOLD | cf2.dwMask;
813   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
814
815   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
816   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
817
818   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
819   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
820
821   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
822   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
823
824   /*Get the formatting of those characters*/
825
826   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
827
828   /*Get the formatting of some other characters*/
829   cf2test.cbSize = sizeof(CHARFORMAT2);
830   cr.cpMin = 21;
831   cr.cpMax = 30;
832   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
833   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
834
835   /*Test that they are the same as plain text allows only one formatting*/
836
837   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
838      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
839      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
840   
841   /*Fill the control with a "wine" string, which when inserted will be bold*/
842
843   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
844
845   /*Copy the bolded "wine" string*/
846
847   cr.cpMin = 0;
848   cr.cpMax = 4;
849   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
850   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
851
852   /*Swap back to rich text*/
853
854   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
855   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
856
857   /*Set the default formatting to bold italics*/
858
859   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
860   cf2.dwMask |= CFM_ITALIC;
861   cf2.dwEffects ^= CFE_ITALIC;
862   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
863   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
864
865   /*Set the text in the control to "wine", which will be bold and italicized*/
866
867   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
868
869   /*Paste the plain text "wine" string, which should take the insert
870    formatting, which at the moment is bold italics*/
871
872   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
873
874   /*Select the first "wine" string and retrieve its formatting*/
875
876   cr.cpMin = 1;
877   cr.cpMax = 3;
878   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
879   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
880
881   /*Select the second "wine" string and retrieve its formatting*/
882
883   cr.cpMin = 5;
884   cr.cpMax = 7;
885   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
886   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
887
888   /*Compare the two formattings. They should be the same.*/
889
890   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
891      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
892      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
893   DestroyWindow(hwndRichEdit);
894 }
895
896 static void test_WM_GETTEXT(void)
897 {
898     HWND hwndRichEdit = new_richedit(NULL);
899     static const char text[] = "Hello. My name is RichEdit!";
900     static const char text2[] = "Hello. My name is RichEdit!\r";
901     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
902     char buffer[1024] = {0};
903     int result;
904
905     /* Baseline test with normal-sized buffer */
906     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
907     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
908     ok(result == lstrlen(buffer),
909         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
910     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
911     result = strcmp(buffer,text);
912     ok(result == 0, 
913         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
914
915     /* Test for returned value of WM_GETTEXTLENGTH */
916     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
917     ok(result == lstrlen(text),
918         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
919         result, lstrlen(text));
920
921     /* Test for behavior in overflow case */
922     memset(buffer, 0, 1024);
923     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
924     ok(result == 0 ||
925        result == lstrlenA(text) - 1, /* XP, win2k3 */
926         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
927     result = strcmp(buffer,text);
928     if (result)
929         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
930     ok(result == 0,
931         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
932
933     /* Baseline test with normal-sized buffer and carriage return */
934     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
935     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
936     ok(result == lstrlen(buffer),
937         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
938     result = strcmp(buffer,text2_after);
939     ok(result == 0,
940         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
941
942     /* Test for returned value of WM_GETTEXTLENGTH */
943     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
944     ok(result == lstrlen(text2_after),
945         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
946         result, lstrlen(text2_after));
947
948     /* Test for behavior of CRLF conversion in case of overflow */
949     memset(buffer, 0, 1024);
950     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
951     ok(result == 0 ||
952        result == lstrlenA(text2) - 1, /* XP, win2k3 */
953         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
954     result = strcmp(buffer,text2);
955     if (result)
956         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
957     ok(result == 0,
958         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
959
960     DestroyWindow(hwndRichEdit);
961 }
962
963 static void test_EM_GETTEXTRANGE(void)
964 {
965     HWND hwndRichEdit = new_richedit(NULL);
966     const char * text1 = "foo bar\r\nfoo bar";
967     const char * text2 = "foo bar\rfoo bar";
968     const char * expect = "bar\rfoo";
969     char buffer[1024] = {0};
970     LRESULT result;
971     TEXTRANGEA textRange;
972
973     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
974
975     textRange.lpstrText = buffer;
976     textRange.chrg.cpMin = 4;
977     textRange.chrg.cpMax = 11;
978     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
979     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
980     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
981
982     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
983
984     textRange.lpstrText = buffer;
985     textRange.chrg.cpMin = 4;
986     textRange.chrg.cpMax = 11;
987     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
988     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
989     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
990
991     DestroyWindow(hwndRichEdit);
992 }
993
994 static void test_EM_GETSELTEXT(void)
995 {
996     HWND hwndRichEdit = new_richedit(NULL);
997     const char * text1 = "foo bar\r\nfoo bar";
998     const char * text2 = "foo bar\rfoo bar";
999     const char * expect = "bar\rfoo";
1000     char buffer[1024] = {0};
1001     LRESULT result;
1002
1003     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1004
1005     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1006     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1007     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1008     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1009
1010     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1011
1012     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1013     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1014     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1015     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1016
1017     DestroyWindow(hwndRichEdit);
1018 }
1019
1020 /* FIXME: need to test unimplemented options and robustly test wparam */
1021 static void test_EM_SETOPTIONS(void)
1022 {
1023     HWND hwndRichEdit = new_richedit(NULL);
1024     static const char text[] = "Hello. My name is RichEdit!";
1025     char buffer[1024] = {0};
1026
1027     /* NEGATIVE TESTING - NO OPTIONS SET */
1028     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1029     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1030
1031     /* testing no readonly by sending 'a' to the control*/
1032     SetFocus(hwndRichEdit);
1033     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1034     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1035     ok(buffer[0]=='a', 
1036        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1037     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1038
1039     /* READONLY - sending 'a' to the control */
1040     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1041     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1042     SetFocus(hwndRichEdit);
1043     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1044     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1045     ok(buffer[0]==text[0], 
1046        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1047
1048     DestroyWindow(hwndRichEdit);
1049 }
1050
1051 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1052 {
1053   CHARFORMAT2W text_format;
1054   text_format.cbSize = sizeof(text_format);
1055   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1056   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1057   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1058 }
1059
1060 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1061 {
1062   int link_present = 0;
1063
1064   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1065   if (is_url) 
1066   { /* control text is url; should get CFE_LINK */
1067         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1068   }
1069   else 
1070   {
1071     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1072   }
1073 }
1074
1075 static HWND new_static_wnd(HWND parent) {
1076   return new_window("Static", 0, parent);
1077 }
1078
1079 static void test_EM_AUTOURLDETECT(void)
1080 {
1081   struct urls_s {
1082     const char *text;
1083     int is_url;
1084   } urls[12] = {
1085     {"winehq.org", 0},
1086     {"http://www.winehq.org", 1},
1087     {"http//winehq.org", 0},
1088     {"ww.winehq.org", 0},
1089     {"www.winehq.org", 1},
1090     {"ftp://192.168.1.1", 1},
1091     {"ftp//192.168.1.1", 0},
1092     {"mailto:your@email.com", 1},    
1093     {"prospero:prosperoserver", 1},
1094     {"telnet:test", 1},
1095     {"news:newserver", 1},
1096     {"wais:waisserver", 1}  
1097   };
1098
1099   int i, j;
1100   int urlRet=-1;
1101   HWND hwndRichEdit, parent;
1102
1103   /* All of the following should cause the URL to be detected  */
1104   const char * templates_delim[] = {
1105     "This is some text with X on it",
1106     "This is some text with (X) on it",
1107     "This is some text with X\r on it",
1108     "This is some text with ---X--- on it",
1109     "This is some text with \"X\" on it",
1110     "This is some text with 'X' on it",
1111     "This is some text with 'X' on it",
1112     "This is some text with :X: on it",
1113
1114     "This text ends with X",
1115
1116     "This is some text with X) on it",
1117     "This is some text with X--- on it",
1118     "This is some text with X\" on it",
1119     "This is some text with X' on it",
1120     "This is some text with X: on it",
1121
1122     "This is some text with (X on it",
1123     "This is some text with \rX on it",
1124     "This is some text with ---X on it",
1125     "This is some text with \"X on it",
1126     "This is some text with 'X on it",
1127     "This is some text with :X on it",
1128   };
1129   /* None of these should cause the URL to be detected */
1130   const char * templates_non_delim[] = {
1131     "This is some text with |X| on it",
1132     "This is some text with *X* on it",
1133     "This is some text with /X/ on it",
1134     "This is some text with +X+ on it",
1135     "This is some text with %X% on it",
1136     "This is some text with #X# on it",
1137     "This is some text with @X@ on it",
1138     "This is some text with \\X\\ on it",
1139     "This is some text with |X on it",
1140     "This is some text with *X on it",
1141     "This is some text with /X on it",
1142     "This is some text with +X on it",
1143     "This is some text with %X on it",
1144     "This is some text with #X on it",
1145     "This is some text with @X on it",
1146     "This is some text with \\X on it",
1147   };
1148   /* All of these cause the URL detection to be extended by one more byte,
1149      thus demonstrating that the tested character is considered as part
1150      of the URL. */
1151   const char * templates_xten_delim[] = {
1152     "This is some text with X| on it",
1153     "This is some text with X* on it",
1154     "This is some text with X/ on it",
1155     "This is some text with X+ on it",
1156     "This is some text with X% on it",
1157     "This is some text with X# on it",
1158     "This is some text with X@ on it",
1159     "This is some text with X\\ on it",
1160   };
1161   char buffer[1024];
1162   MSG msg;
1163
1164   parent = new_static_wnd(NULL);
1165   hwndRichEdit = new_richedit(parent);
1166   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1167   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1168   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1169   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1170   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1171   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1172   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1173   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1174   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1175   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1176   /* for each url, check the text to see if CFE_LINK effect is present */
1177   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1178
1179     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1180     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1181     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1182
1183     /* Link detection should happen immediately upon WM_SETTEXT */
1184     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1185     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1186     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1187   }
1188   DestroyWindow(hwndRichEdit);
1189
1190   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1191   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1192     hwndRichEdit = new_richedit(parent);
1193
1194     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1195       char * at_pos;
1196       int at_offset;
1197       int end_offset;
1198
1199       at_pos = strchr(templates_delim[j], 'X');
1200       at_offset = at_pos - templates_delim[j];
1201       strncpy(buffer, templates_delim[j], at_offset);
1202       buffer[at_offset] = '\0';
1203       strcat(buffer, urls[i].text);
1204       strcat(buffer, templates_delim[j] + at_offset + 1);
1205       end_offset = at_offset + strlen(urls[i].text);
1206
1207       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1208       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1209
1210       /* This assumes no templates start with the URL itself, and that they
1211          have at least two characters before the URL text */
1212       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1213         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1214       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1215         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1216       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1217         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1218
1219       if (urls[i].is_url)
1220       {
1221         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1222           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1223         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1224           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1225       }
1226       else
1227       {
1228         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1229           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1230         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1231           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1232       }
1233       if (buffer[end_offset] != '\0')
1234       {
1235         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1236           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1237         if (buffer[end_offset +1] != '\0')
1238         {
1239           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1240             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1241         }
1242       }
1243     }
1244
1245     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1246       char * at_pos;
1247       int at_offset;
1248       int end_offset;
1249
1250       at_pos = strchr(templates_non_delim[j], 'X');
1251       at_offset = at_pos - templates_non_delim[j];
1252       strncpy(buffer, templates_non_delim[j], at_offset);
1253       buffer[at_offset] = '\0';
1254       strcat(buffer, urls[i].text);
1255       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1256       end_offset = at_offset + strlen(urls[i].text);
1257
1258       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1259       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1260
1261       /* This assumes no templates start with the URL itself, and that they
1262          have at least two characters before the URL text */
1263       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1264         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1265       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1266         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1267       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1268         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1269
1270       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1271         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1272       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1273         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1274       if (buffer[end_offset] != '\0')
1275       {
1276         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1277           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1278         if (buffer[end_offset +1] != '\0')
1279         {
1280           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1281             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1282         }
1283       }
1284     }
1285
1286     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1287       char * at_pos;
1288       int at_offset;
1289       int end_offset;
1290
1291       at_pos = strchr(templates_xten_delim[j], 'X');
1292       at_offset = at_pos - templates_xten_delim[j];
1293       strncpy(buffer, templates_xten_delim[j], at_offset);
1294       buffer[at_offset] = '\0';
1295       strcat(buffer, urls[i].text);
1296       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1297       end_offset = at_offset + strlen(urls[i].text);
1298
1299       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1300       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1301
1302       /* This assumes no templates start with the URL itself, and that they
1303          have at least two characters before the URL text */
1304       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1305         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1306       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1307         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1308       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1309         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1310
1311       if (urls[i].is_url)
1312       {
1313         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1314           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1315         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1316           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1317         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1318           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1319       }
1320       else
1321       {
1322         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1323           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1324         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1325           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1326         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1327           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1328       }
1329       if (buffer[end_offset +1] != '\0')
1330       {
1331         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1332           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1333         if (buffer[end_offset +2] != '\0')
1334         {
1335           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1336             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1337         }
1338       }
1339     }
1340
1341     DestroyWindow(hwndRichEdit);
1342     hwndRichEdit = NULL;
1343   }
1344
1345 #define INSERT_CR \
1346   do { \
1347     keybd_event('\r', 0x1c, 0, 0); \
1348     keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
1349     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1350       TranslateMessage(&msg); \
1351       DispatchMessage(&msg); \
1352     } \
1353   } while (0)
1354
1355 #define INSERT_BS \
1356   do { \
1357     keybd_event(0x08, 0x0e, 0, 0); \
1358     keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
1359     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1360       TranslateMessage(&msg); \
1361       DispatchMessage(&msg); \
1362     } \
1363   } while (0)
1364
1365   /* Test detection of URLs within normal text - WM_CHAR case. */
1366   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1367     hwndRichEdit = new_richedit(parent);
1368
1369     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1370       char * at_pos;
1371       int at_offset;
1372       int end_offset;
1373       int u, v;
1374
1375       at_pos = strchr(templates_delim[j], 'X');
1376       at_offset = at_pos - templates_delim[j];
1377       end_offset = at_offset + strlen(urls[i].text);
1378
1379       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1380       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1381       for (u = 0; templates_delim[j][u]; u++) {
1382         if (templates_delim[j][u] == '\r') {
1383           INSERT_CR;
1384         } else if (templates_delim[j][u] != 'X') {
1385           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1386         } else {
1387           for (v = 0; urls[i].text[v]; v++) {
1388             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1389           }
1390         }
1391       }
1392       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1393       trace("Using template: %s\n", templates_delim[j]);
1394
1395       /* This assumes no templates start with the URL itself, and that they
1396          have at least two characters before the URL text */
1397       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1398         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1399       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1400         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1401       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1402         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1403
1404       if (urls[i].is_url)
1405       {
1406         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1407           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1408         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1409           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1410       }
1411       else
1412       {
1413         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1414           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1415         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1416           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1417       }
1418       if (buffer[end_offset] != '\0')
1419       {
1420         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1421           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1422         if (buffer[end_offset +1] != '\0')
1423         {
1424           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1425             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1426         }
1427       }
1428
1429       /* The following will insert a paragraph break after the first character
1430          of the URL candidate, thus breaking the URL. It is expected that the
1431          CFE_LINK attribute should break across both pieces of the URL */
1432       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1433       INSERT_CR;
1434       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1435
1436       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1437         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1438       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1439         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1440       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1441         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1442
1443       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1444         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1445       /* end_offset moved because of paragraph break */
1446       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1447         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1448       if (buffer[end_offset+1] != '\0')
1449       {
1450         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1451           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1452         if (buffer[end_offset +2] != '\0')
1453         {
1454           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1455             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1456         }
1457       }
1458
1459       /* The following will remove the just-inserted paragraph break, thus
1460          restoring the URL */
1461       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1462       INSERT_BS;
1463       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1464
1465       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1466         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1467       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1468         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1469       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1470         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1471
1472       if (urls[i].is_url)
1473       {
1474         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1475           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1476         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1477           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1478       }
1479       else
1480       {
1481         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1482           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1483         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1484           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1485       }
1486       if (buffer[end_offset] != '\0')
1487       {
1488         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1489           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1490         if (buffer[end_offset +1] != '\0')
1491         {
1492           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1493             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1494         }
1495       }
1496     }
1497     DestroyWindow(hwndRichEdit);
1498     hwndRichEdit = NULL;
1499   }
1500
1501   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1502   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1503     SETTEXTEX st;
1504
1505     hwndRichEdit = new_richedit(parent);
1506
1507     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1508        be detected:
1509        1) Set entire text, a la WM_SETTEXT
1510        2) Set a selection of the text to the URL
1511        3) Set a portion of the text at a time, which eventually results in
1512           an URL
1513        All of them should give equivalent results
1514      */
1515
1516     /* Set entire text in one go, like WM_SETTEXT */
1517     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1518       char * at_pos;
1519       int at_offset;
1520       int end_offset;
1521
1522       st.codepage = CP_ACP;
1523       st.flags = ST_DEFAULT;
1524
1525       at_pos = strchr(templates_delim[j], 'X');
1526       at_offset = at_pos - templates_delim[j];
1527       strncpy(buffer, templates_delim[j], at_offset);
1528       buffer[at_offset] = '\0';
1529       strcat(buffer, urls[i].text);
1530       strcat(buffer, templates_delim[j] + at_offset + 1);
1531       end_offset = at_offset + strlen(urls[i].text);
1532
1533       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1534       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1535
1536       /* This assumes no templates start with the URL itself, and that they
1537          have at least two characters before the URL text */
1538       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1539         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1540       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1541         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1542       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1543         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1544
1545       if (urls[i].is_url)
1546       {
1547         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1548           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1549         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1550           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1551       }
1552       else
1553       {
1554         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1555           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1556         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1557           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1558       }
1559       if (buffer[end_offset] != '\0')
1560       {
1561         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1562           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1563         if (buffer[end_offset +1] != '\0')
1564         {
1565           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1566             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1567         }
1568       }
1569     }
1570
1571     /* Set selection with X to the URL */
1572     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1573       char * at_pos;
1574       int at_offset;
1575       int end_offset;
1576
1577       at_pos = strchr(templates_delim[j], 'X');
1578       at_offset = at_pos - templates_delim[j];
1579       end_offset = at_offset + strlen(urls[i].text);
1580
1581       st.codepage = CP_ACP;
1582       st.flags = ST_DEFAULT;
1583       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1584       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1585       st.flags = ST_SELECTION;
1586       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1587       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1588       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1589
1590       /* This assumes no templates start with the URL itself, and that they
1591          have at least two characters before the URL text */
1592       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1593         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1594       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1595         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1596       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1597         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1598
1599       if (urls[i].is_url)
1600       {
1601         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1602           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1603         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1604           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1605       }
1606       else
1607       {
1608         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1609           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1610         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1611           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1612       }
1613       if (buffer[end_offset] != '\0')
1614       {
1615         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1616           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1617         if (buffer[end_offset +1] != '\0')
1618         {
1619           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1620             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1621         }
1622       }
1623     }
1624
1625     /* Set selection with X to the first character of the URL, then the rest */
1626     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1627       char * at_pos;
1628       int at_offset;
1629       int end_offset;
1630
1631       at_pos = strchr(templates_delim[j], 'X');
1632       at_offset = at_pos - templates_delim[j];
1633       end_offset = at_offset + strlen(urls[i].text);
1634
1635       strcpy(buffer, "YY");
1636       buffer[0] = urls[i].text[0];
1637
1638       st.codepage = CP_ACP;
1639       st.flags = ST_DEFAULT;
1640       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1641       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1642       st.flags = ST_SELECTION;
1643       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1644       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1645       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1646       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1647       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1648
1649       /* This assumes no templates start with the URL itself, and that they
1650          have at least two characters before the URL text */
1651       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1652         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1653       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1654         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1655       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1656         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1657
1658       if (urls[i].is_url)
1659       {
1660         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1661           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1662         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1663           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1664       }
1665       else
1666       {
1667         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1668           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1669         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1670           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1671       }
1672       if (buffer[end_offset] != '\0')
1673       {
1674         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1675           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1676         if (buffer[end_offset +1] != '\0')
1677         {
1678           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1679             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1680         }
1681       }
1682     }
1683
1684     DestroyWindow(hwndRichEdit);
1685     hwndRichEdit = NULL;
1686   }
1687
1688   DestroyWindow(parent);
1689 }
1690
1691 static void test_EM_SCROLL(void)
1692 {
1693   int i, j;
1694   int r; /* return value */
1695   int expr; /* expected return value */
1696   HWND hwndRichEdit = new_richedit(NULL);
1697   int y_before, y_after; /* units of lines of text */
1698
1699   /* test a richedit box containing a single line of text */
1700   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
1701   expr = 0x00010000;
1702   for (i = 0; i < 4; i++) {
1703     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
1704
1705     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
1706     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1707     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
1708        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
1709     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
1710        "(i == %d)\n", y_after, i);
1711   }
1712
1713   /*
1714    * test a richedit box that will scroll. There are two general
1715    * cases: the case without any long lines and the case with a long
1716    * line.
1717    */
1718   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1719     if (i == 0)
1720       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1721     else
1722       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1723                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1724                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1725                   "LONG LINE \nb\nc\nd\ne");
1726     for (j = 0; j < 12; j++) /* reset scroll position to top */
1727       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1728
1729     /* get first visible line */
1730     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1731     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1732
1733     /* get new current first visible line */
1734     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1735
1736     ok(((r & 0xffffff00) == 0x00010000) &&
1737        ((r & 0x000000ff) != 0x00000000),
1738        "EM_SCROLL page down didn't scroll by a small positive number of "
1739        "lines (r == 0x%08x)\n", r);
1740     ok(y_after > y_before, "EM_SCROLL page down not functioning "
1741        "(line %d scrolled to line %d\n", y_before, y_after);
1742
1743     y_before = y_after;
1744     
1745     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1746     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1747     ok(((r & 0xffffff00) == 0x0001ff00),
1748        "EM_SCROLL page up didn't scroll by a small negative number of lines "
1749        "(r == 0x%08x)\n", r);
1750     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1751        "%d scrolled to line %d\n", y_before, y_after);
1752     
1753     y_before = y_after;
1754
1755     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1756
1757     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1758
1759     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1760        "(r == 0x%08x)\n", r);
1761     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1762        "1 line (%d scrolled to %d)\n", y_before, y_after);
1763
1764     y_before = y_after;
1765
1766     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1767
1768     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1769
1770     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1771        "(r == 0x%08x)\n", r);
1772     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1773        "line (%d scrolled to %d)\n", y_before, y_after);
1774
1775     y_before = y_after;
1776
1777     r = SendMessage(hwndRichEdit, EM_SCROLL,
1778                     SB_LINEUP, 0); /* lineup beyond top */
1779
1780     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1781
1782     ok(r == 0x00010000,
1783        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1784     ok(y_before == y_after,
1785        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1786
1787     y_before = y_after;
1788
1789     r = SendMessage(hwndRichEdit, EM_SCROLL,
1790                     SB_PAGEUP, 0);/*page up beyond top */
1791
1792     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1793
1794     ok(r == 0x00010000,
1795        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1796     ok(y_before == y_after,
1797        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1798
1799     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1800       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1801     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1802     r = SendMessage(hwndRichEdit, EM_SCROLL,
1803                     SB_PAGEDOWN, 0); /* page down beyond bot */
1804     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1805
1806     ok(r == 0x00010000,
1807        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1808     ok(y_before == y_after,
1809        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1810        y_before, y_after);
1811
1812     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1813     SendMessage(hwndRichEdit, EM_SCROLL,
1814                 SB_LINEDOWN, 0); /* line down beyond bot */
1815     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1816     
1817     ok(r == 0x00010000,
1818        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1819     ok(y_before == y_after,
1820        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1821        y_before, y_after);
1822   }
1823   DestroyWindow(hwndRichEdit);
1824 }
1825
1826 static void test_EM_SETUNDOLIMIT(void)
1827 {
1828   /* cases we test for:
1829    * default behaviour - limiting at 100 undo's 
1830    * undo disabled - setting a limit of 0
1831    * undo limited -  undo limit set to some to some number, like 2
1832    * bad input - sending a negative number should default to 100 undo's */
1833  
1834   HWND hwndRichEdit = new_richedit(NULL);
1835   CHARRANGE cr;
1836   int i;
1837   int result;
1838   
1839   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1840   cr.cpMin = 0;
1841   cr.cpMax = 1;
1842   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1843     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1844       also, multiple pastes don't combine like WM_CHAR would */
1845   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1846
1847   /* first case - check the default */
1848   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1849   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1850     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1851   for (i=0; i<100; i++) /* Undo 100 of them */
1852     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1853   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1854      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1855
1856   /* second case - cannot undo */
1857   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1858   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
1859   SendMessage(hwndRichEdit,
1860               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1861   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1862      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1863
1864   /* third case - set it to an arbitrary number */
1865   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1866   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
1867   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1868   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1869   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1870   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1871   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1872      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1873   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1874   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1875      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1876   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1877   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1878      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1879   
1880   /* fourth case - setting negative numbers should default to 100 undos */
1881   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1882   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1883   ok (result == 100, 
1884       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1885       
1886   DestroyWindow(hwndRichEdit);
1887 }
1888
1889 static void test_ES_PASSWORD(void)
1890 {
1891   /* This isn't hugely testable, so we're just going to run it through its paces */
1892
1893   HWND hwndRichEdit = new_richedit(NULL);
1894   WCHAR result;
1895
1896   /* First, check the default of a regular control */
1897   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1898   ok (result == 0,
1899         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1900
1901   /* Now, set it to something normal */
1902   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1903   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1904   ok (result == 120,
1905         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1906
1907   /* Now, set it to something odd */
1908   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1909   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1910   ok (result == 1234,
1911         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1912   DestroyWindow(hwndRichEdit);
1913 }
1914
1915 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1916                                          LPBYTE pbBuff,
1917                                          LONG cb,
1918                                          LONG *pcb)
1919 {
1920   char** str = (char**)dwCookie;
1921   *pcb = cb;
1922   if (*pcb > 0) {
1923     memcpy(*str, pbBuff, *pcb);
1924     *str += *pcb;
1925   }
1926   return 0;
1927 }
1928
1929 static void test_WM_SETTEXT()
1930 {
1931   HWND hwndRichEdit = new_richedit(NULL);
1932   const char * TestItem1 = "TestSomeText";
1933   const char * TestItem2 = "TestSomeText\r";
1934   const char * TestItem2_after = "TestSomeText\r\n";
1935   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1936   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1937   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1938   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1939   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1940   const char * TestItem5_after = "TestSomeText TestSomeText";
1941   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1942   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1943   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1944   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1945
1946   char buf[1024] = {0};
1947   LRESULT result;
1948   EDITSTREAM es;
1949   char * p;
1950
1951   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1952      any solitary \r to be converted to \r\n on return. Properly paired
1953      \r\n are not affected. It also shows that the special sequence \r\r\n
1954      gets converted to a single space.
1955    */
1956
1957 #define TEST_SETTEXT(a, b) \
1958   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1959   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1960   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1961   ok (result == lstrlen(buf), \
1962         "WM_GETTEXT returned %ld instead of expected %u\n", \
1963         result, lstrlen(buf)); \
1964   result = strcmp(b, buf); \
1965   ok(result == 0, \
1966         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1967
1968   TEST_SETTEXT(TestItem1, TestItem1)
1969   TEST_SETTEXT(TestItem2, TestItem2_after)
1970   TEST_SETTEXT(TestItem3, TestItem3_after)
1971   TEST_SETTEXT(TestItem3_after, TestItem3_after)
1972   TEST_SETTEXT(TestItem4, TestItem4_after)
1973   TEST_SETTEXT(TestItem5, TestItem5_after)
1974   TEST_SETTEXT(TestItem6, TestItem6_after)
1975   TEST_SETTEXT(TestItem7, TestItem7_after)
1976
1977   /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1978   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1979   p = buf;
1980   es.dwCookie = (DWORD_PTR)&p;
1981   es.dwError = 0;
1982   es.pfnCallback = test_WM_SETTEXT_esCallback;
1983   memset(buf, 0, sizeof(buf));
1984   SendMessage(hwndRichEdit, EM_STREAMOUT,
1985               (WPARAM)(SF_RTF), (LPARAM)&es);
1986   trace("EM_STREAMOUT produced: \n%s\n", buf);
1987   TEST_SETTEXT(buf, TestItem1)
1988
1989 #undef TEST_SETTEXT
1990   DestroyWindow(hwndRichEdit);
1991 }
1992
1993 static void test_EM_STREAMOUT(void)
1994 {
1995   HWND hwndRichEdit = new_richedit(NULL);
1996   int r;
1997   EDITSTREAM es;
1998   char buf[1024] = {0};
1999   char * p;
2000
2001   const char * TestItem1 = "TestSomeText";
2002   const char * TestItem2 = "TestSomeText\r";
2003   const char * TestItem3 = "TestSomeText\r\n";
2004
2005   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2006   p = buf;
2007   es.dwCookie = (DWORD_PTR)&p;
2008   es.dwError = 0;
2009   es.pfnCallback = test_WM_SETTEXT_esCallback;
2010   memset(buf, 0, sizeof(buf));
2011   SendMessage(hwndRichEdit, EM_STREAMOUT,
2012               (WPARAM)(SF_TEXT), (LPARAM)&es);
2013   r = strlen(buf);
2014   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2015   ok(strcmp(buf, TestItem1) == 0,
2016         "streamed text different, got %s\n", buf);
2017
2018   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2019   p = buf;
2020   es.dwCookie = (DWORD_PTR)&p;
2021   es.dwError = 0;
2022   es.pfnCallback = test_WM_SETTEXT_esCallback;
2023   memset(buf, 0, sizeof(buf));
2024   SendMessage(hwndRichEdit, EM_STREAMOUT,
2025               (WPARAM)(SF_TEXT), (LPARAM)&es);
2026   r = strlen(buf);
2027   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2028   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2029   ok(strcmp(buf, TestItem3) == 0,
2030         "streamed text different from, got %s\n", buf);
2031   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2032   p = buf;
2033   es.dwCookie = (DWORD_PTR)&p;
2034   es.dwError = 0;
2035   es.pfnCallback = test_WM_SETTEXT_esCallback;
2036   memset(buf, 0, sizeof(buf));
2037   SendMessage(hwndRichEdit, EM_STREAMOUT,
2038               (WPARAM)(SF_TEXT), (LPARAM)&es);
2039   r = strlen(buf);
2040   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2041   ok(strcmp(buf, TestItem3) == 0,
2042         "streamed text different, got %s\n", buf);
2043
2044   DestroyWindow(hwndRichEdit);
2045 }
2046
2047 static void test_EM_SETTEXTEX(void)
2048 {
2049   HWND hwndRichEdit = new_richedit(NULL);
2050   SETTEXTEX setText;
2051   GETTEXTEX getText;
2052   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
2053                        'S', 'o', 'm', 'e', 
2054                        'T', 'e', 'x', 't', 0}; 
2055   WCHAR TestItem2[] = {'T', 'e', 's', 't',
2056                        'S', 'o', 'm', 'e',
2057                        'T', 'e', 'x', 't',
2058                       '\r', 0};
2059   const char * TestItem2_after = "TestSomeText\r\n";
2060   WCHAR TestItem3[] = {'T', 'e', 's', 't',
2061                        'S', 'o', 'm', 'e',
2062                        'T', 'e', 'x', 't',
2063                       '\r','\n','\r','\n', 0};
2064   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2065                        'S', 'o', 'm', 'e',
2066                        'T', 'e', 'x', 't',
2067                        '\n','\n', 0};
2068   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2069                        'S', 'o', 'm', 'e',
2070                        'T', 'e', 'x', 't',
2071                        '\r','\r', 0};
2072   WCHAR TestItem4[] = {'T', 'e', 's', 't',
2073                        'S', 'o', 'm', 'e',
2074                        'T', 'e', 'x', 't',
2075                       '\r','\r','\n','\r',
2076                       '\n', 0};
2077   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2078                        'S', 'o', 'm', 'e',
2079                        'T', 'e', 'x', 't',
2080                        ' ','\r', 0};
2081 #define MAX_BUF_LEN 1024
2082   WCHAR buf[MAX_BUF_LEN];
2083   char * p;
2084   int result;
2085   CHARRANGE cr;
2086   EDITSTREAM es;
2087
2088   setText.codepage = 1200;  /* no constant for unicode */
2089   getText.codepage = 1200;  /* no constant for unicode */
2090   getText.cb = MAX_BUF_LEN;
2091   getText.flags = GT_DEFAULT;
2092   getText.lpDefaultChar = NULL;
2093   getText.lpUsedDefChar = NULL;
2094
2095   setText.flags = 0;
2096   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2097   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2098   ok(lstrcmpW(buf, TestItem1) == 0,
2099       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2100
2101   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2102      convert \r to \r\n on return
2103    */
2104   setText.codepage = 1200;  /* no constant for unicode */
2105   getText.codepage = 1200;  /* no constant for unicode */
2106   getText.cb = MAX_BUF_LEN;
2107   getText.flags = GT_DEFAULT;
2108   getText.lpDefaultChar = NULL;
2109   getText.lpUsedDefChar = NULL;
2110   setText.flags = 0;
2111   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2112   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2113   ok(lstrcmpW(buf, TestItem2) == 0,
2114       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2115
2116   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2117   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2118   ok(strcmp((const char *)buf, TestItem2_after) == 0,
2119       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2120
2121   /* Baseline test for just-enough buffer space for string */
2122   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2123   getText.codepage = 1200;  /* no constant for unicode */
2124   getText.flags = GT_DEFAULT;
2125   getText.lpDefaultChar = NULL;
2126   getText.lpUsedDefChar = NULL;
2127   memset(buf, 0, MAX_BUF_LEN);
2128   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2129   ok(lstrcmpW(buf, TestItem2) == 0,
2130       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2131
2132   /* When there is enough space for one character, but not both, of the CRLF
2133      pair at the end of the string, the CR is not copied at all. That is,
2134      the caller must not see CRLF pairs truncated to CR at the end of the
2135      string.
2136    */
2137   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2138   getText.codepage = 1200;  /* no constant for unicode */
2139   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
2140   getText.lpDefaultChar = NULL;
2141   getText.lpUsedDefChar = NULL;
2142   memset(buf, 0, MAX_BUF_LEN);
2143   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2144   ok(lstrcmpW(buf, TestItem1) == 0,
2145       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2146
2147
2148   /* \r\n pairs get changed into \r */
2149   setText.codepage = 1200;  /* no constant for unicode */
2150   getText.codepage = 1200;  /* no constant for unicode */
2151   getText.cb = MAX_BUF_LEN;
2152   getText.flags = GT_DEFAULT;
2153   getText.lpDefaultChar = NULL;
2154   getText.lpUsedDefChar = NULL;
2155   setText.flags = 0;
2156   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2157   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2158   ok(lstrcmpW(buf, TestItem3_after) == 0,
2159       "EM_SETTEXTEX did not convert properly\n");
2160
2161   /* \n also gets changed to \r */
2162   setText.codepage = 1200;  /* no constant for unicode */
2163   getText.codepage = 1200;  /* no constant for unicode */
2164   getText.cb = MAX_BUF_LEN;
2165   getText.flags = GT_DEFAULT;
2166   getText.lpDefaultChar = NULL;
2167   getText.lpUsedDefChar = NULL;
2168   setText.flags = 0;
2169   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2170   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2171   ok(lstrcmpW(buf, TestItem3_after) == 0,
2172       "EM_SETTEXTEX did not convert properly\n");
2173
2174   /* \r\r\n gets changed into single space */
2175   setText.codepage = 1200;  /* no constant for unicode */
2176   getText.codepage = 1200;  /* no constant for unicode */
2177   getText.cb = MAX_BUF_LEN;
2178   getText.flags = GT_DEFAULT;
2179   getText.lpDefaultChar = NULL;
2180   getText.lpUsedDefChar = NULL;
2181   setText.flags = 0;
2182   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2183   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2184   ok(lstrcmpW(buf, TestItem4_after) == 0,
2185       "EM_SETTEXTEX did not convert properly\n");
2186
2187   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
2188                        (WPARAM)&setText, (LPARAM) NULL);
2189   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2190   
2191   ok (result == 1, 
2192       "EM_SETTEXTEX returned %d, instead of 1\n",result);
2193   ok(lstrlenW(buf) == 0,
2194       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2195   
2196   /* put some text back */
2197   setText.flags = 0;
2198   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2199   /* select some text */
2200   cr.cpMax = 1;
2201   cr.cpMin = 3;
2202   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2203   /* replace current selection */
2204   setText.flags = ST_SELECTION;
2205   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
2206                        (WPARAM)&setText, (LPARAM) NULL);
2207   ok(result == 0,
2208       "EM_SETTEXTEX with NULL lParam to replace selection"
2209       " with no text should return 0. Got %i\n",
2210       result);
2211   
2212   /* put some text back */
2213   setText.flags = 0;
2214   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2215   /* select some text */
2216   cr.cpMax = 1;
2217   cr.cpMin = 3;
2218   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2219   /* replace current selection */
2220   setText.flags = ST_SELECTION;
2221   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2222                        (WPARAM)&setText, (LPARAM) TestItem1);
2223   /* get text */
2224   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2225   ok(result == lstrlenW(TestItem1),
2226       "EM_SETTEXTEX with NULL lParam to replace selection"
2227       " with no text should return 0. Got %i\n",
2228       result);
2229   ok(lstrlenW(buf) == 22,
2230       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2231       lstrlenW(buf) );
2232
2233   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2234   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2235   p = (char *)buf;
2236   es.dwCookie = (DWORD_PTR)&p;
2237   es.dwError = 0;
2238   es.pfnCallback = test_WM_SETTEXT_esCallback;
2239   memset(buf, 0, sizeof(buf));
2240   SendMessage(hwndRichEdit, EM_STREAMOUT,
2241               (WPARAM)(SF_RTF), (LPARAM)&es);
2242   trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2243
2244   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2245   getText.codepage = 1200;  /* no constant for unicode */
2246   getText.cb = MAX_BUF_LEN;
2247   getText.flags = GT_DEFAULT;
2248   getText.lpDefaultChar = NULL;
2249   getText.lpUsedDefChar = NULL;
2250
2251   setText.flags = 0;
2252   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2253   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2254   ok(lstrcmpW(buf, TestItem1) == 0,
2255       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2256
2257
2258   DestroyWindow(hwndRichEdit);
2259 }
2260
2261 static void test_EM_LIMITTEXT(void)
2262 {
2263   int ret;
2264
2265   HWND hwndRichEdit = new_richedit(NULL);
2266
2267   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2268    * about setting the length to -1 for multiline edit controls doesn't happen.
2269    */
2270
2271   /* Don't check default gettextlimit case. That's done in other tests */
2272
2273   /* Set textlimit to 100 */
2274   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2275   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2276   ok (ret == 100,
2277       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2278
2279   /* Set textlimit to 0 */
2280   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2281   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2282   ok (ret == 65536,
2283       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2284
2285   /* Set textlimit to -1 */
2286   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2287   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2288   ok (ret == -1,
2289       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2290
2291   /* Set textlimit to -2 */
2292   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2293   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2294   ok (ret == -2,
2295       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2296
2297   DestroyWindow (hwndRichEdit);
2298 }
2299
2300
2301 static void test_EM_EXLIMITTEXT(void)
2302 {
2303   int i, selBegin, selEnd, len1, len2;
2304   int result;
2305   char text[1024 + 1];
2306   char buffer[1024 + 1];
2307   int textlimit = 0; /* multiple of 100 */
2308   HWND hwndRichEdit = new_richedit(NULL);
2309   
2310   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2311   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2312   
2313   textlimit = 256000;
2314   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2315   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2316   /* set higher */
2317   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2318   
2319   textlimit = 1000;
2320   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2321   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2322   /* set lower */
2323   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2324  
2325   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2326   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2327   /* default for WParam = 0 */
2328   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2329  
2330   textlimit = sizeof(text)-1;
2331   memset(text, 'W', textlimit);
2332   text[sizeof(text)-1] = 0;
2333   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2334   /* maxed out text */
2335   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2336   
2337   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
2338   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2339   len1 = selEnd - selBegin;
2340   
2341   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2342   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2343   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2344   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2345   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2346   len2 = selEnd - selBegin;
2347   
2348   ok(len1 != len2,
2349     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2350     len1,len2,i);
2351   
2352   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2353   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2354   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2355   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2356   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2357   len1 = selEnd - selBegin;
2358   
2359   ok(len1 != len2,
2360     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2361     len1,len2,i);
2362   
2363   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2364   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2365   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
2366   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2367   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2368   len2 = selEnd - selBegin;
2369   
2370   ok(len1 == len2, 
2371     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2372     len1,len2,i);
2373
2374   /* set text up to the limit, select all the text, then add a char */
2375   textlimit = 5;
2376   memset(text, 'W', textlimit);
2377   text[textlimit] = 0;
2378   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2379   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2380   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2381   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2382   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2383   result = strcmp(buffer, "A");
2384   ok(0 == result, "got string = \"%s\"\n", buffer);
2385
2386   /* WM_SETTEXT not limited */
2387   textlimit = 10;
2388   memset(text, 'W', textlimit);
2389   text[textlimit] = 0;
2390   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2391   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2392   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2393   i = strlen(buffer);
2394   ok(10 == i, "expected 10 chars\n");
2395   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2396   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2397
2398   /* try inserting more text at end */
2399   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2400   ok(0 == i, "WM_CHAR wasn't processed\n");
2401   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2402   i = strlen(buffer);
2403   ok(10 == i, "expected 10 chars, got %i\n", i);
2404   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2405   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2406
2407   /* try inserting text at beginning */
2408   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2409   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2410   ok(0 == i, "WM_CHAR wasn't processed\n");
2411   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2412   i = strlen(buffer);
2413   ok(10 == i, "expected 10 chars, got %i\n", i);
2414   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2415   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2416
2417   /* WM_CHAR is limited */
2418   textlimit = 1;
2419   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2420   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
2421   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2422   ok(0 == i, "WM_CHAR wasn't processed\n");
2423   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2424   ok(0 == i, "WM_CHAR wasn't processed\n");
2425   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2426   i = strlen(buffer);
2427   ok(1 == i, "expected 1 chars, got %i instead\n", i);
2428
2429   DestroyWindow(hwndRichEdit);
2430 }
2431
2432 static void test_EM_GETLIMITTEXT(void)
2433 {
2434   int i;
2435   HWND hwndRichEdit = new_richedit(NULL);
2436
2437   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2438   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2439
2440   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2441   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2442   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2443
2444   DestroyWindow(hwndRichEdit);
2445 }
2446
2447 static void test_WM_SETFONT(void)
2448 {
2449   /* There is no invalid input or error conditions for this function.
2450    * NULL wParam and lParam just fall back to their default values 
2451    * It should be noted that even if you use a gibberish name for your fonts
2452    * here, it will still work because the name is stored. They will display as
2453    * System, but will report their name to be whatever they were created as */
2454   
2455   HWND hwndRichEdit = new_richedit(NULL);
2456   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2457     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2458     FF_DONTCARE, "Marlett");
2459   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2460     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2461     FF_DONTCARE, "MS Sans Serif");
2462   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2463     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2464     FF_DONTCARE, "Courier");
2465   LOGFONTA sentLogFont;
2466   CHARFORMAT2A returnedCF2A;
2467   
2468   returnedCF2A.cbSize = sizeof(returnedCF2A);
2469   
2470   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2471   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2472   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2473
2474   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2475   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2476     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2477     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2478
2479   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2480   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2481   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2482   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2483     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2484     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2485     
2486   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2487   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2488   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2489   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2490     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2491     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2492    
2493   /* This last test is special since we send in NULL. We clear the variables
2494    * and just compare to "System" instead of the sent in font name. */
2495   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2496   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2497   returnedCF2A.cbSize = sizeof(returnedCF2A);
2498   
2499   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2500   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2501   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2502   ok (!strcmp("System",returnedCF2A.szFaceName),
2503     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2504   
2505   DestroyWindow(hwndRichEdit);
2506 }
2507
2508
2509 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2510                                          LPBYTE pbBuff,
2511                                          LONG cb,
2512                                          LONG *pcb)
2513 {
2514   const char** str = (const char**)dwCookie;
2515   int size = strlen(*str);
2516   if(size > 3)  /* let's make it piecemeal for fun */
2517     size = 3;
2518   *pcb = cb;
2519   if (*pcb > size) {
2520     *pcb = size;
2521   }
2522   if (*pcb > 0) {
2523     memcpy(pbBuff, *str, *pcb);
2524     *str += *pcb;
2525   }
2526   return 0;
2527 }
2528
2529 static void test_EM_GETMODIFY(void)
2530 {
2531   HWND hwndRichEdit = new_richedit(NULL);
2532   LRESULT result;
2533   SETTEXTEX setText;
2534   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
2535                        'S', 'o', 'm', 'e', 
2536                        'T', 'e', 'x', 't', 0}; 
2537   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
2538                        'S', 'o', 'm', 'e', 
2539                        'O', 't', 'h', 'e', 'r',
2540                        'T', 'e', 'x', 't', 0}; 
2541   const char* streamText = "hello world";
2542   CHARFORMAT2 cf2;
2543   PARAFORMAT2 pf2;
2544   EDITSTREAM es;
2545   
2546   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2547     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2548     FF_DONTCARE, "Courier");
2549   
2550   setText.codepage = 1200;  /* no constant for unicode */
2551   setText.flags = ST_KEEPUNDO;
2552   
2553
2554   /* modify flag shouldn't be set when richedit is first created */
2555   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2556   ok (result == 0, 
2557       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2558   
2559   /* setting modify flag should actually set it */
2560   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2561   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2562   ok (result != 0, 
2563       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2564   
2565   /* clearing modify flag should actually clear it */
2566   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2567   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2568   ok (result == 0, 
2569       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2570  
2571   /* setting font doesn't change modify flag */
2572   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2573   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2574   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2575   ok (result == 0,
2576       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2577
2578   /* setting text should set modify flag */
2579   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2580   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2581   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2582   ok (result != 0,
2583       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2584   
2585   /* undo previous text doesn't reset modify flag */
2586   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2587   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2588   ok (result != 0,
2589       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2590   
2591   /* set text with no flag to keep undo stack should not set modify flag */
2592   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2593   setText.flags = 0;
2594   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2595   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2596   ok (result == 0,
2597       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2598   
2599   /* WM_SETTEXT doesn't modify */
2600   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2601   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2602   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2603   ok (result == 0,
2604       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2605   
2606   /* clear the text */
2607   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2608   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2609   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2610   ok (result == 0,
2611       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2612   
2613   /* replace text */
2614   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2615   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2616   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2617   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
2618   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2619   ok (result != 0,
2620       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
2621   
2622   /* copy/paste text 1 */
2623   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2624   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2625   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2626   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2627   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2628   ok (result != 0,
2629       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
2630   
2631   /* copy/paste text 2 */
2632   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2633   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2634   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2635   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
2636   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2637   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2638   ok (result != 0,
2639       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
2640   
2641   /* press char */
2642   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2643   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
2644   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2645   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2646   ok (result != 0,
2647       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
2648
2649   /* press del */
2650   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2651   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2652   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
2653   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2654   ok (result != 0,
2655       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
2656   
2657   /* set char format */
2658   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2659   cf2.cbSize = sizeof(CHARFORMAT2);
2660   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
2661              (LPARAM) &cf2);
2662   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
2663   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
2664   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2665   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2666   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
2667   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2668   ok (result != 0,
2669       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
2670   
2671   /* set para format */
2672   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2673   pf2.cbSize = sizeof(PARAFORMAT2);
2674   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
2675              (LPARAM) &pf2);
2676   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
2677   pf2.wAlignment = PFA_RIGHT;
2678   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
2679   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2680   ok (result == 0,
2681       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
2682
2683   /* EM_STREAM */
2684   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2685   es.dwCookie = (DWORD_PTR)&streamText;
2686   es.dwError = 0;
2687   es.pfnCallback = test_EM_GETMODIFY_esCallback;
2688   SendMessage(hwndRichEdit, EM_STREAMIN, 
2689               (WPARAM)(SF_TEXT), (LPARAM)&es);
2690   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2691   ok (result != 0,
2692       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
2693
2694   DestroyWindow(hwndRichEdit);
2695 }
2696
2697 struct exsetsel_s {
2698   long min;
2699   long max;
2700   long expected_retval;
2701   int expected_getsel_start;
2702   int expected_getsel_end;
2703   int _exsetsel_todo_wine;
2704   int _getsel_todo_wine;
2705 };
2706
2707 const struct exsetsel_s exsetsel_tests[] = {
2708   /* sanity tests */
2709   {5, 10, 10, 5, 10, 0, 0},
2710   {15, 17, 17, 15, 17, 0, 0},
2711   /* test cpMax > strlen() */
2712   {0, 100, 18, 0, 18, 0, 1},
2713   /* test cpMin == cpMax */
2714   {5, 5, 5, 5, 5, 0, 0},
2715   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2716   {-1, 0, 5, 5, 5, 0, 0},
2717   {-1, 17, 5, 5, 5, 0, 0},
2718   {-1, 18, 5, 5, 5, 0, 0},
2719   /* test cpMin < 0 && cpMax < 0 */
2720   {-1, -1, 17, 17, 17, 0, 0},
2721   {-4, -5, 17, 17, 17, 0, 0},
2722   /* test cMin >=0 && cpMax < 0 (bug 6814) */
2723   {0, -1, 18, 0, 18, 0, 1},
2724   {17, -5, 18, 17, 18, 0, 1},
2725   {18, -3, 17, 17, 17, 0, 0},
2726   /* test if cpMin > cpMax */
2727   {15, 19, 18, 15, 18, 0, 1},
2728   {19, 15, 18, 15, 18, 0, 1}
2729 };
2730
2731 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2732     CHARRANGE cr;
2733     long result;
2734     int start, end;
2735
2736     cr.cpMin = setsel->min;
2737     cr.cpMax = setsel->max;
2738     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2739
2740     if (setsel->_exsetsel_todo_wine) {
2741         todo_wine {
2742             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2743         }
2744     } else {
2745         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2746     }
2747
2748     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2749
2750     if (setsel->_getsel_todo_wine) {
2751         todo_wine {
2752             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);
2753         }
2754     } else {
2755         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);
2756     }
2757 }
2758
2759 static void test_EM_EXSETSEL(void)
2760 {
2761     HWND hwndRichEdit = new_richedit(NULL);
2762     int i;
2763     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2764
2765     /* sending some text to the window */
2766     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2767     /*                                                 01234567890123456*/
2768     /*                                                          10      */
2769
2770     for (i = 0; i < num_tests; i++) {
2771         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2772     }
2773
2774     DestroyWindow(hwndRichEdit);
2775 }
2776
2777 static void test_EM_REPLACESEL(int redraw)
2778 {
2779     HWND hwndRichEdit = new_richedit(NULL);
2780     char buffer[1024] = {0};
2781     int r;
2782     GETTEXTEX getText;
2783     CHARRANGE cr;
2784
2785     /* sending some text to the window */
2786     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2787     /*                                                 01234567890123456*/
2788     /*                                                          10      */
2789
2790     /* FIXME add more tests */
2791     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2792     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2793     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2794     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2795     r = strcmp(buffer, "testing");
2796     ok(0 == r, "expected %d, got %d\n", 0, r);
2797
2798     DestroyWindow(hwndRichEdit);
2799
2800     hwndRichEdit = new_richedit(NULL);
2801
2802     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2803     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2804
2805     /* Test behavior with carriage returns and newlines */
2806     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2807     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2808     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2809     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2810     r = strcmp(buffer, "RichEdit1");
2811     ok(0 == r, "expected %d, got %d\n", 0, r);
2812     getText.cb = 1024;
2813     getText.codepage = CP_ACP;
2814     getText.flags = GT_DEFAULT;
2815     getText.lpDefaultChar = NULL;
2816     getText.lpUsedDefChar = NULL;
2817     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2818     ok(strcmp(buffer, "RichEdit1") == 0,
2819       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2820
2821     /* Test number of lines reported after EM_REPLACESEL */
2822     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2823     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2824
2825     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2826     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2827     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2828     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2829     r = strcmp(buffer, "RichEdit1\r\n");
2830     ok(0 == r, "expected %d, got %d\n", 0, r);
2831     getText.cb = 1024;
2832     getText.codepage = CP_ACP;
2833     getText.flags = GT_DEFAULT;
2834     getText.lpDefaultChar = NULL;
2835     getText.lpUsedDefChar = NULL;
2836     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2837     ok(strcmp(buffer, "RichEdit1\r") == 0,
2838       "EM_GETTEXTEX returned incorrect string\n");
2839
2840     /* Test number of lines reported after EM_REPLACESEL */
2841     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2842     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2843
2844     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2845        EM_REPLACESEL. The general rule seems to be that Win98's riched20
2846        returns the number of characters *inserted* into the control (after
2847        required conversions), but WinXP's riched20 returns the number of
2848        characters interpreted from the original lParam. Wine's builtin riched20
2849        implements the WinXP behavior.
2850      */
2851     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2852     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2853     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2854         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2855
2856     /* Test number of lines reported after EM_REPLACESEL */
2857     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2858     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2859
2860     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2861     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2862     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2863     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2864
2865     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2866     r = strcmp(buffer, "RichEdit1\r\n");
2867     ok(0 == r, "expected %d, got %d\n", 0, r);
2868     getText.cb = 1024;
2869     getText.codepage = CP_ACP;
2870     getText.flags = GT_DEFAULT;
2871     getText.lpDefaultChar = NULL;
2872     getText.lpUsedDefChar = NULL;
2873     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2874     ok(strcmp(buffer, "RichEdit1\r") == 0,
2875       "EM_GETTEXTEX returned incorrect string\n");
2876
2877     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2878     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2879     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2880     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2881
2882     /* The following tests show that richedit should handle the special \r\r\n
2883        sequence by turning it into a single space on insertion. However,
2884        EM_REPLACESEL on WinXP returns the number of characters in the original
2885        string.
2886      */
2887
2888     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2889     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2890     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2891     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2892     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2893     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2894     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2895
2896     /* Test the actual string */
2897     getText.cb = 1024;
2898     getText.codepage = CP_ACP;
2899     getText.flags = GT_DEFAULT;
2900     getText.lpDefaultChar = NULL;
2901     getText.lpUsedDefChar = NULL;
2902     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2903     ok(strcmp(buffer, "\r\r") == 0,
2904       "EM_GETTEXTEX returned incorrect string\n");
2905
2906     /* Test number of lines reported after EM_REPLACESEL */
2907     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2908     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2909
2910     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2911     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2912     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2913         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2914     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2915     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2916     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2917     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2918
2919     /* Test the actual string */
2920     getText.cb = 1024;
2921     getText.codepage = CP_ACP;
2922     getText.flags = GT_DEFAULT;
2923     getText.lpDefaultChar = NULL;
2924     getText.lpUsedDefChar = NULL;
2925     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2926     ok(strcmp(buffer, " ") == 0,
2927       "EM_GETTEXTEX returned incorrect string\n");
2928
2929     /* Test number of lines reported after EM_REPLACESEL */
2930     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2931     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2932
2933     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2934     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2935     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2936         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2937     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2938     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2939     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2940     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2941
2942     /* Test the actual string */
2943     getText.cb = 1024;
2944     getText.codepage = CP_ACP;
2945     getText.flags = GT_DEFAULT;
2946     getText.lpDefaultChar = NULL;
2947     getText.lpUsedDefChar = NULL;
2948     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2949     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2950       "EM_GETTEXTEX returned incorrect string\n");
2951
2952     /* Test number of lines reported after EM_REPLACESEL */
2953     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2954     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2955
2956     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2957     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2958     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2959         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2960     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2961     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2962     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2963     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2964
2965     /* Test the actual string */
2966     getText.cb = 1024;
2967     getText.codepage = CP_ACP;
2968     getText.flags = GT_DEFAULT;
2969     getText.lpDefaultChar = NULL;
2970     getText.lpUsedDefChar = NULL;
2971     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2972     ok(strcmp(buffer, " \r") == 0,
2973       "EM_GETTEXTEX returned incorrect string\n");
2974
2975     /* Test number of lines reported after EM_REPLACESEL */
2976     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2977     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2978
2979     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2980     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2981     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2982         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2983     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2984     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2985     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2986     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2987
2988     /* Test the actual string */
2989     getText.cb = 1024;
2990     getText.codepage = CP_ACP;
2991     getText.flags = GT_DEFAULT;
2992     getText.lpDefaultChar = NULL;
2993     getText.lpUsedDefChar = NULL;
2994     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2995     ok(strcmp(buffer, " \r\r") == 0,
2996       "EM_GETTEXTEX returned incorrect string\n");
2997
2998     /* Test number of lines reported after EM_REPLACESEL */
2999     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3000     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3001
3002     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3003     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3004     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3005         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3006     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3007     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3008     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3009     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3010
3011     /* Test the actual string */
3012     getText.cb = 1024;
3013     getText.codepage = CP_ACP;
3014     getText.flags = GT_DEFAULT;
3015     getText.lpDefaultChar = NULL;
3016     getText.lpUsedDefChar = NULL;
3017     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3018     ok(strcmp(buffer, "\rX\r\r\r") == 0,
3019       "EM_GETTEXTEX returned incorrect string\n");
3020
3021     /* Test number of lines reported after EM_REPLACESEL */
3022     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3023     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3024
3025     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3026     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3027     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3028     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3029     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3030     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3031     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3032
3033     /* Test the actual string */
3034     getText.cb = 1024;
3035     getText.codepage = CP_ACP;
3036     getText.flags = GT_DEFAULT;
3037     getText.lpDefaultChar = NULL;
3038     getText.lpUsedDefChar = NULL;
3039     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3040     ok(strcmp(buffer, "\r\r") == 0,
3041       "EM_GETTEXTEX returned incorrect string\n");
3042
3043     /* Test number of lines reported after EM_REPLACESEL */
3044     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3045     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3046
3047     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3048     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3049     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3050         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3051     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3052     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3053     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3054     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3055
3056     /* Test the actual string */
3057     getText.cb = 1024;
3058     getText.codepage = CP_ACP;
3059     getText.flags = GT_DEFAULT;
3060     getText.lpDefaultChar = NULL;
3061     getText.lpUsedDefChar = NULL;
3062     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3063     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3064       "EM_GETTEXTEX returned incorrect string\n");
3065
3066     /* Test number of lines reported after EM_REPLACESEL */
3067     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3068     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3069
3070     DestroyWindow(hwndRichEdit);
3071 }
3072
3073 static void test_WM_PASTE(void)
3074 {
3075     MSG msg;
3076     int result;
3077     char buffer[1024] = {0};
3078     char key_info[][3] =
3079     {
3080         /* VirtualKey, ScanCode, WM_CHAR code */
3081         {'C', 0x2e,  3},        /* Ctrl-C */
3082         {'X', 0x2d, 24},        /* Ctrl-X */
3083         {'V', 0x2f, 22},        /* Ctrl-V */
3084         {'Z', 0x2c, 26},        /* Ctrl-Z */
3085         {'Y', 0x15, 25},        /* Ctrl-Y */
3086     };
3087     const char* text1 = "testing paste\r";
3088     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3089     const char* text1_after = "testing paste\r\n";
3090     const char* text2 = "testing paste\r\rtesting paste";
3091     const char* text2_after = "testing paste\r\n\r\ntesting paste";
3092     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3093     HWND hwndRichEdit = new_richedit(NULL);
3094
3095     /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3096        messages, probably because it inspects the keyboard state itself.
3097        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3098      */
3099 #define SEND_CTRL_KEY(hwnd, k) \
3100     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
3101     keybd_event(k[0], k[1], 0, 0);\
3102     keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
3103     keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
3104     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
3105         TranslateMessage(&msg); \
3106         DispatchMessage(&msg); \
3107     }
3108
3109 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
3110 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
3111 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
3112 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
3113 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
3114
3115     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3116     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3117
3118     SEND_CTRL_C(hwndRichEdit)   /* Copy */
3119     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3120     SEND_CTRL_V(hwndRichEdit)   /* Paste */
3121     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3122     /* Pasted text should be visible at this step */
3123     result = strcmp(text1_step1, buffer);
3124     ok(result == 0,
3125         "test paste: strcmp = %i\n", result);
3126     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
3127     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3128     /* Text should be the same as before (except for \r -> \r\n conversion) */
3129     result = strcmp(text1_after, buffer);
3130     ok(result == 0,
3131         "test paste: strcmp = %i\n", result);
3132
3133     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3134     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3135     SEND_CTRL_C(hwndRichEdit)   /* Copy */
3136     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3137     SEND_CTRL_V(hwndRichEdit)   /* Paste */
3138     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3139     /* Pasted text should be visible at this step */
3140     result = strcmp(text3, buffer);
3141     ok(result == 0,
3142         "test paste: strcmp = %i\n", result);
3143     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
3144     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3145     /* Text should be the same as before (except for \r -> \r\n conversion) */
3146     result = strcmp(text2_after, buffer);
3147     ok(result == 0,
3148         "test paste: strcmp = %i\n", result);
3149     SEND_CTRL_Y(hwndRichEdit)   /* Redo */
3150     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3151     /* Text should revert to post-paste state */
3152     result = strcmp(buffer,text3);
3153     ok(result == 0,
3154         "test paste: strcmp = %i\n", result);
3155
3156     DestroyWindow(hwndRichEdit);
3157 }
3158
3159 static void test_EM_FORMATRANGE(void)
3160 {
3161   int r;
3162   FORMATRANGE fr;
3163   HDC hdc;
3164   HWND hwndRichEdit = new_richedit(NULL);
3165
3166   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3167
3168   hdc = GetDC(hwndRichEdit);
3169   ok(hdc != NULL, "Could not get HDC\n");
3170
3171   fr.hdc = fr.hdcTarget = hdc;
3172   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3173   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3174   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3175   fr.chrg.cpMin = 0;
3176   fr.chrg.cpMax = 20;
3177
3178   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3179   todo_wine {
3180     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3181   }
3182
3183   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3184   todo_wine {
3185     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3186   }
3187
3188   fr.chrg.cpMin = 0;
3189   fr.chrg.cpMax = 10;
3190
3191   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3192   todo_wine {
3193     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3194   }
3195
3196   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3197   todo_wine {
3198     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3199   }
3200
3201   DestroyWindow(hwndRichEdit);
3202 }
3203
3204 static int nCallbackCount = 0;
3205
3206 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3207                                  LONG cb, LONG* pcb)
3208 {
3209   const char text[] = {'t','e','s','t'};
3210
3211   if (sizeof(text) <= cb)
3212   {
3213     if ((int)dwCookie != nCallbackCount)
3214     {
3215       *pcb = 0;
3216       return 0;
3217     }
3218
3219     memcpy (pbBuff, text, sizeof(text));
3220     *pcb = sizeof(text);
3221
3222     nCallbackCount++;
3223
3224     return 0;
3225   }
3226   else
3227     return 1; /* indicates callback failed */
3228 }
3229
3230 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3231                                          LPBYTE pbBuff,
3232                                          LONG cb,
3233                                          LONG *pcb)
3234 {
3235   const char** str = (const char**)dwCookie;
3236   int size = strlen(*str);
3237   *pcb = cb;
3238   if (*pcb > size) {
3239     *pcb = size;
3240   }
3241   if (*pcb > 0) {
3242     memcpy(pbBuff, *str, *pcb);
3243     *str += *pcb;
3244   }
3245   return 0;
3246 }
3247
3248
3249 static void test_EM_STREAMIN(void)
3250 {
3251   HWND hwndRichEdit = new_richedit(NULL);
3252   LRESULT result;
3253   EDITSTREAM es;
3254   char buffer[1024] = {0};
3255
3256   const char * streamText0 = "{\\rtf1 TestSomeText}";
3257   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3258   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3259
3260   const char * streamText1 =
3261   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3262   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3263   "}\r\n";
3264
3265   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3266   const char * streamText2 =
3267     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3268     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3269     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3270     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3271     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3272     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3273     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3274
3275   const char * streamText3 = "RichEdit1";
3276
3277   /* Minimal test without \par at the end */
3278   es.dwCookie = (DWORD_PTR)&streamText0;
3279   es.dwError = 0;
3280   es.pfnCallback = test_EM_STREAMIN_esCallback;
3281   SendMessage(hwndRichEdit, EM_STREAMIN,
3282               (WPARAM)(SF_RTF), (LPARAM)&es);
3283
3284   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3285   ok (result  == 12,
3286       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3287   result = strcmp (buffer,"TestSomeText");
3288   ok (result  == 0,
3289       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3290   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3291
3292   /* Native richedit 2.0 ignores last \par */
3293   es.dwCookie = (DWORD_PTR)&streamText0a;
3294   es.dwError = 0;
3295   es.pfnCallback = test_EM_STREAMIN_esCallback;
3296   SendMessage(hwndRichEdit, EM_STREAMIN,
3297               (WPARAM)(SF_RTF), (LPARAM)&es);
3298
3299   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3300   ok (result  == 12,
3301       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3302   result = strcmp (buffer,"TestSomeText");
3303   ok (result  == 0,
3304       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3305   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3306
3307   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3308   es.dwCookie = (DWORD_PTR)&streamText0b;
3309   es.dwError = 0;
3310   es.pfnCallback = test_EM_STREAMIN_esCallback;
3311   SendMessage(hwndRichEdit, EM_STREAMIN,
3312               (WPARAM)(SF_RTF), (LPARAM)&es);
3313
3314   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3315   ok (result  == 14,
3316       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3317   result = strcmp (buffer,"TestSomeText\r\n");
3318   ok (result  == 0,
3319       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3320   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3321
3322   es.dwCookie = (DWORD_PTR)&streamText1;
3323   es.dwError = 0;
3324   es.pfnCallback = test_EM_STREAMIN_esCallback;
3325   SendMessage(hwndRichEdit, EM_STREAMIN,
3326               (WPARAM)(SF_RTF), (LPARAM)&es);
3327
3328   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3329   ok (result  == 12,
3330       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3331   result = strcmp (buffer,"TestSomeText");
3332   ok (result  == 0,
3333       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3334   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3335
3336   es.dwCookie = (DWORD_PTR)&streamText2;
3337   es.dwError = 0;
3338   SendMessage(hwndRichEdit, EM_STREAMIN,
3339               (WPARAM)(SF_RTF), (LPARAM)&es);
3340
3341   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3342   ok (result  == 0,
3343       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3344   ok (strlen(buffer)  == 0,
3345       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3346   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3347
3348   es.dwCookie = (DWORD_PTR)&streamText3;
3349   es.dwError = 0;
3350   SendMessage(hwndRichEdit, EM_STREAMIN,
3351               (WPARAM)(SF_RTF), (LPARAM)&es);
3352
3353   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3354   ok (result  == 0,
3355       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3356   ok (strlen(buffer)  == 0,
3357       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3358   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3359
3360   DestroyWindow(hwndRichEdit);
3361 }
3362
3363 static void test_EM_StreamIn_Undo(void)
3364 {
3365   /* The purpose of this test is to determine when a EM_StreamIn should be
3366    * undoable. This is important because WM_PASTE currently uses StreamIn and
3367    * pasting should always be undoable but streaming isn't always.
3368    *
3369    * cases to test:
3370    * StreamIn plain text without SFF_SELECTION.
3371    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3372    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3373    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3374    * Feel free to add tests for other text modes or StreamIn things.
3375    */
3376
3377
3378   HWND hwndRichEdit = new_richedit(NULL);
3379   LRESULT result;
3380   EDITSTREAM es;
3381   char buffer[1024] = {0};
3382   const char randomtext[] = "Some text";
3383
3384   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3385
3386   /* StreamIn, no SFF_SELECTION */
3387   es.dwCookie = nCallbackCount;
3388   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3389   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3390   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3391   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3392   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3393   result = strcmp (buffer,"test");
3394   ok (result  == 0,
3395       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3396
3397   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3398   ok (result == FALSE,
3399       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3400
3401   /* StreamIn, SFF_SELECTION, but nothing selected */
3402   es.dwCookie = nCallbackCount;
3403   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3404   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3405   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3406   SendMessage(hwndRichEdit, EM_STREAMIN,
3407               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3408   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3409   result = strcmp (buffer,"testSome text");
3410   ok (result  == 0,
3411       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3412
3413   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3414   ok (result == TRUE,
3415      "EM_STREAMIN with SFF_SELECTION but no selection set "
3416       "should create an undo\n");
3417
3418   /* StreamIn, SFF_SELECTION, with a selection */
3419   es.dwCookie = nCallbackCount;
3420   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3421   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3422   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3423   SendMessage(hwndRichEdit, EM_STREAMIN,
3424               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3425   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3426   result = strcmp (buffer,"Sometesttext");
3427   ok (result  == 0,
3428       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3429
3430   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3431   ok (result == TRUE,
3432       "EM_STREAMIN with SFF_SELECTION and selection set "
3433       "should create an undo\n");
3434
3435 }
3436
3437 static BOOL is_em_settextex_supported(HWND hwnd)
3438 {
3439     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3440     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3441 }
3442
3443 static void test_unicode_conversions(void)
3444 {
3445     static const WCHAR tW[] = {'t',0};
3446     static const WCHAR teW[] = {'t','e',0};
3447     static const WCHAR textW[] = {'t','e','s','t',0};
3448     static const char textA[] = "test";
3449     char bufA[64];
3450     WCHAR bufW[64];
3451     HWND hwnd;
3452     int is_win9x, em_settextex_supported, ret;
3453
3454     is_win9x = GetVersion() & 0x80000000;
3455
3456 #define set_textA(hwnd, wm_set_text, txt) \
3457     do { \
3458         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3459         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3460         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3461         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3462         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3463     } while(0)
3464 #define expect_textA(hwnd, wm_get_text, txt) \
3465     do { \
3466         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3467         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3468         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3469         memset(bufA, 0xAA, sizeof(bufA)); \
3470         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3471         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3472         ret = lstrcmpA(bufA, txt); \
3473         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3474     } while(0)
3475
3476 #define set_textW(hwnd, wm_set_text, txt) \
3477     do { \
3478         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3479         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3480         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3481         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3482         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3483     } while(0)
3484 #define expect_textW(hwnd, wm_get_text, txt) \
3485     do { \
3486         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3487         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3488         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3489         memset(bufW, 0xAA, sizeof(bufW)); \
3490         if (is_win9x) \
3491         { \
3492             assert(wm_get_text == EM_GETTEXTEX); \
3493             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3494             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3495         } \
3496         else \
3497         { \
3498             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3499             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3500         } \
3501         ret = lstrcmpW(bufW, txt); \
3502         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3503     } while(0)
3504 #define expect_empty(hwnd, wm_get_text) \
3505     do { \
3506         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3507         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3508         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3509         memset(bufA, 0xAA, sizeof(bufA)); \
3510         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3511         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3512         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3513     } while(0)
3514
3515     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3516                            0, 0, 200, 60, 0, 0, 0, 0);
3517     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3518
3519     ret = IsWindowUnicode(hwnd);
3520     if (is_win9x)
3521         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3522     else
3523         ok(ret, "RichEdit20W should be unicode under NT\n");
3524
3525     /* EM_SETTEXTEX is supported starting from version 3.0 */
3526     em_settextex_supported = is_em_settextex_supported(hwnd);
3527     trace("EM_SETTEXTEX is %ssupported on this platform\n",
3528           em_settextex_supported ? "" : "NOT ");
3529
3530     expect_empty(hwnd, WM_GETTEXT);
3531     expect_empty(hwnd, EM_GETTEXTEX);
3532
3533     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3534     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3535     expect_textA(hwnd, WM_GETTEXT, "t");
3536     expect_textA(hwnd, EM_GETTEXTEX, "t");
3537     expect_textW(hwnd, EM_GETTEXTEX, tW);
3538
3539     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3540     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3541     expect_textA(hwnd, WM_GETTEXT, "te");
3542     expect_textA(hwnd, EM_GETTEXTEX, "te");
3543     expect_textW(hwnd, EM_GETTEXTEX, teW);
3544
3545     set_textA(hwnd, WM_SETTEXT, NULL);
3546     expect_empty(hwnd, WM_GETTEXT);
3547     expect_empty(hwnd, EM_GETTEXTEX);
3548
3549     if (is_win9x)
3550         set_textA(hwnd, WM_SETTEXT, textW);
3551     else
3552         set_textA(hwnd, WM_SETTEXT, textA);
3553     expect_textA(hwnd, WM_GETTEXT, textA);
3554     expect_textA(hwnd, EM_GETTEXTEX, textA);
3555     expect_textW(hwnd, EM_GETTEXTEX, textW);
3556
3557     if (em_settextex_supported)
3558     {
3559         set_textA(hwnd, EM_SETTEXTEX, textA);
3560         expect_textA(hwnd, WM_GETTEXT, textA);
3561         expect_textA(hwnd, EM_GETTEXTEX, textA);
3562         expect_textW(hwnd, EM_GETTEXTEX, textW);
3563     }
3564
3565     if (!is_win9x)
3566     {
3567         set_textW(hwnd, WM_SETTEXT, textW);
3568         expect_textW(hwnd, WM_GETTEXT, textW);
3569         expect_textA(hwnd, WM_GETTEXT, textA);
3570         expect_textW(hwnd, EM_GETTEXTEX, textW);
3571         expect_textA(hwnd, EM_GETTEXTEX, textA);
3572
3573         if (em_settextex_supported)
3574         {
3575             set_textW(hwnd, EM_SETTEXTEX, textW);
3576             expect_textW(hwnd, WM_GETTEXT, textW);
3577             expect_textA(hwnd, WM_GETTEXT, textA);
3578             expect_textW(hwnd, EM_GETTEXTEX, textW);
3579             expect_textA(hwnd, EM_GETTEXTEX, textA);
3580         }
3581     }
3582     DestroyWindow(hwnd);
3583
3584     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
3585                            0, 0, 200, 60, 0, 0, 0, 0);
3586     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3587
3588     ret = IsWindowUnicode(hwnd);
3589     ok(!ret, "RichEdit20A should NOT be unicode\n");
3590
3591     set_textA(hwnd, WM_SETTEXT, textA);
3592     expect_textA(hwnd, WM_GETTEXT, textA);
3593     expect_textA(hwnd, EM_GETTEXTEX, textA);
3594     expect_textW(hwnd, EM_GETTEXTEX, textW);
3595
3596     if (em_settextex_supported)
3597     {
3598         set_textA(hwnd, EM_SETTEXTEX, textA);
3599         expect_textA(hwnd, WM_GETTEXT, textA);
3600         expect_textA(hwnd, EM_GETTEXTEX, textA);
3601         expect_textW(hwnd, EM_GETTEXTEX, textW);
3602     }
3603
3604     if (!is_win9x)
3605     {
3606         set_textW(hwnd, WM_SETTEXT, textW);
3607         expect_textW(hwnd, WM_GETTEXT, textW);
3608         expect_textA(hwnd, WM_GETTEXT, textA);
3609         expect_textW(hwnd, EM_GETTEXTEX, textW);
3610         expect_textA(hwnd, EM_GETTEXTEX, textA);
3611
3612         if (em_settextex_supported)
3613         {
3614             set_textW(hwnd, EM_SETTEXTEX, textW);
3615             expect_textW(hwnd, WM_GETTEXT, textW);
3616             expect_textA(hwnd, WM_GETTEXT, textA);
3617             expect_textW(hwnd, EM_GETTEXTEX, textW);
3618             expect_textA(hwnd, EM_GETTEXTEX, textA);
3619         }
3620     }
3621     DestroyWindow(hwnd);
3622 }
3623
3624 static void test_WM_CHAR(void)
3625 {
3626     HWND hwnd;
3627     int ret;
3628     const char * char_list = "abc\rabc\r";
3629     const char * expected_content_single = "abcabc";
3630     const char * expected_content_multi = "abc\r\nabc\r\n";
3631     char buffer[64] = {0};
3632     const char * p;
3633
3634     /* single-line control must IGNORE carriage returns */
3635     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3636                            0, 0, 200, 60, 0, 0, 0, 0);
3637     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3638
3639     p = char_list;
3640     while (*p != '\0') {
3641         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3642         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3643         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3644         SendMessageA(hwnd, WM_KEYUP, *p, 1);
3645         p++;
3646     }
3647
3648     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3649     ret = strcmp(buffer, expected_content_single);
3650     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3651
3652     DestroyWindow(hwnd);
3653
3654     /* multi-line control inserts CR normally */
3655     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
3656                            0, 0, 200, 60, 0, 0, 0, 0);
3657     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3658
3659     p = char_list;
3660     while (*p != '\0') {
3661         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3662         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3663         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3664         SendMessageA(hwnd, WM_KEYUP, *p, 1);
3665         p++;
3666     }
3667
3668     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3669     ret = strcmp(buffer, expected_content_multi);
3670     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3671
3672     DestroyWindow(hwnd);
3673 }
3674
3675 static void test_EM_GETTEXTLENGTHEX(void)
3676 {
3677     HWND hwnd;
3678     GETTEXTLENGTHEX gtl;
3679     int ret;
3680     const char * base_string = "base string";
3681     const char * test_string = "a\nb\n\n\r\n";
3682     const char * test_string_after = "a";
3683     const char * test_string_2 = "a\rtest\rstring";
3684     char buffer[64] = {0};
3685
3686     /* single line */
3687     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3688                            0, 0, 200, 60, 0, 0, 0, 0);
3689     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3690
3691     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3692     gtl.codepage = CP_ACP;
3693     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3694     ok(ret == 0, "ret %d\n",ret);
3695
3696     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3697     gtl.codepage = CP_ACP;
3698     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3699     ok(ret == 0, "ret %d\n",ret);
3700
3701     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3702
3703     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3704     gtl.codepage = CP_ACP;
3705     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3706     ok(ret == strlen(base_string), "ret %d\n",ret);
3707
3708     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3709     gtl.codepage = CP_ACP;
3710     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3711     ok(ret == strlen(base_string), "ret %d\n",ret);
3712
3713     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3714
3715     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3716     gtl.codepage = CP_ACP;
3717     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3718     ok(ret == 1, "ret %d\n",ret);
3719
3720     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3721     gtl.codepage = CP_ACP;
3722     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3723     ok(ret == 1, "ret %d\n",ret);
3724
3725     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3726     ret = strcmp(buffer, test_string_after);
3727     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3728
3729     DestroyWindow(hwnd);
3730
3731     /* multi line */
3732     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3733                            0, 0, 200, 60, 0, 0, 0, 0);
3734     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3735
3736     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3737     gtl.codepage = CP_ACP;
3738     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3739     ok(ret == 0, "ret %d\n",ret);
3740
3741     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3742     gtl.codepage = CP_ACP;
3743     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3744     ok(ret == 0, "ret %d\n",ret);
3745
3746     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3747
3748     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3749     gtl.codepage = CP_ACP;
3750     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3751     ok(ret == strlen(base_string), "ret %d\n",ret);
3752
3753     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3754     gtl.codepage = CP_ACP;
3755     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3756     ok(ret == strlen(base_string), "ret %d\n",ret);
3757
3758     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3759
3760     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3761     gtl.codepage = CP_ACP;
3762     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3763     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3764
3765     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3766     gtl.codepage = CP_ACP;
3767     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3768     ok(ret == strlen(test_string_2), "ret %d\n",ret);
3769
3770     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3771
3772     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3773     gtl.codepage = CP_ACP;
3774     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3775     ok(ret == 10, "ret %d\n",ret);
3776
3777     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3778     gtl.codepage = CP_ACP;
3779     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3780     ok(ret == 6, "ret %d\n",ret);
3781
3782     DestroyWindow(hwnd);
3783 }
3784
3785
3786 /* globals that parent and child access when checking event masks & notifications */
3787 static HWND eventMaskEditHwnd = 0;
3788 static int queriedEventMask;
3789 static int watchForEventMask = 0;
3790
3791 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3792 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3793 {
3794     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3795     {
3796       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3797     }
3798     return DefWindowProcA(hwnd, message, wParam, lParam);
3799 }
3800
3801 /* test event masks in combination with WM_COMMAND */
3802 static void test_eventMask(void)
3803 {
3804     HWND parent;
3805     int ret;
3806     WNDCLASSA cls;
3807     const char text[] = "foo bar\n";
3808     int eventMask;
3809
3810     /* register class to capture WM_COMMAND */
3811     cls.style = 0;
3812     cls.lpfnWndProc = ParentMsgCheckProcA;
3813     cls.cbClsExtra = 0;
3814     cls.cbWndExtra = 0;
3815     cls.hInstance = GetModuleHandleA(0);
3816     cls.hIcon = 0;
3817     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3818     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3819     cls.lpszMenuName = NULL;
3820     cls.lpszClassName = "EventMaskParentClass";
3821     if(!RegisterClassA(&cls)) assert(0);
3822
3823     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3824                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3825     ok (parent != 0, "Failed to create parent window\n");
3826
3827     eventMaskEditHwnd = new_richedit(parent);
3828     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3829
3830     eventMask = ENM_CHANGE | ENM_UPDATE;
3831     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3832     ok(ret == ENM_NONE, "wrong event mask\n");
3833     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3834     ok(ret == eventMask, "failed to set event mask\n");
3835
3836     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3837     queriedEventMask = 0;  /* initialize to something other than we expect */
3838     watchForEventMask = EN_CHANGE;
3839     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3840     ok(ret == TRUE, "failed to set text\n");
3841     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3842        notification in response to WM_SETTEXT */
3843     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3844             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3845
3846 }
3847
3848 static int received_WM_NOTIFY = 0;
3849 static int modify_at_WM_NOTIFY = 0;
3850 static HWND hwndRichedit_WM_NOTIFY;
3851
3852 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3853 {
3854     if(message == WM_NOTIFY)
3855     {
3856       received_WM_NOTIFY = 1;
3857       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3858     }
3859     return DefWindowProcA(hwnd, message, wParam, lParam);
3860 }
3861
3862 static void test_WM_NOTIFY(void)
3863 {
3864     HWND parent;
3865     WNDCLASSA cls;
3866     CHARFORMAT2 cf2;
3867
3868     /* register class to capture WM_NOTIFY */
3869     cls.style = 0;
3870     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3871     cls.cbClsExtra = 0;
3872     cls.cbWndExtra = 0;
3873     cls.hInstance = GetModuleHandleA(0);
3874     cls.hIcon = 0;
3875     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3876     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3877     cls.lpszMenuName = NULL;
3878     cls.lpszClassName = "WM_NOTIFY_ParentClass";
3879     if(!RegisterClassA(&cls)) assert(0);
3880
3881     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3882                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3883     ok (parent != 0, "Failed to create parent window\n");
3884
3885     hwndRichedit_WM_NOTIFY = new_richedit(parent);
3886     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3887
3888     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3889
3890     /* Notifications for selection change should only be sent when selection
3891        actually changes. EM_SETCHARFORMAT is one message that calls
3892        ME_CommitUndo, which should check whether message should be sent */
3893     received_WM_NOTIFY = 0;
3894     cf2.cbSize = sizeof(CHARFORMAT2);
3895     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3896              (LPARAM) &cf2);
3897     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3898     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3899     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3900     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3901
3902     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3903        already at 0. */
3904     received_WM_NOTIFY = 0;
3905     modify_at_WM_NOTIFY = 0;
3906     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3907     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3908     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3909
3910     received_WM_NOTIFY = 0;
3911     modify_at_WM_NOTIFY = 0;
3912     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3913     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3914
3915     received_WM_NOTIFY = 0;
3916     modify_at_WM_NOTIFY = 0;
3917     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3918     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3919     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3920
3921     DestroyWindow(hwndRichedit_WM_NOTIFY);
3922     DestroyWindow(parent);
3923 }
3924
3925 START_TEST( editor )
3926 {
3927   MSG msg;
3928   time_t end;
3929
3930   /* Must explicitly LoadLibrary(). The test has no references to functions in
3931    * RICHED20.DLL, so the linker doesn't actually link to it. */
3932   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3933   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3934   test_WM_CHAR();
3935   test_EM_FINDTEXT();
3936   test_EM_GETLINE();
3937   test_EM_SCROLLCARET();
3938   test_EM_SCROLL();
3939   test_WM_SETTEXT();
3940   test_EM_LINELENGTH();
3941   test_EM_SETCHARFORMAT();
3942   test_EM_SETTEXTMODE();
3943   test_TM_PLAINTEXT();
3944   test_EM_SETOPTIONS();
3945   test_WM_GETTEXT();
3946   test_EM_GETTEXTRANGE();
3947   test_EM_GETSELTEXT();
3948   test_EM_SETUNDOLIMIT();
3949   test_ES_PASSWORD();
3950   test_EM_SETTEXTEX();
3951   test_EM_LIMITTEXT();
3952   test_EM_EXLIMITTEXT();
3953   test_EM_GETLIMITTEXT();
3954   test_WM_SETFONT();
3955   test_EM_GETMODIFY();
3956   test_EM_EXSETSEL();
3957   test_WM_PASTE();
3958   test_EM_AUTOURLDETECT();
3959   test_EM_STREAMIN();
3960   test_EM_STREAMOUT();
3961   test_EM_StreamIn_Undo();
3962   test_EM_FORMATRANGE();
3963   test_unicode_conversions();
3964   test_EM_GETTEXTLENGTHEX();
3965   test_EM_REPLACESEL(1);
3966   test_EM_REPLACESEL(0);
3967   test_WM_NOTIFY();
3968   test_eventMask();
3969
3970   /* Set the environment variable WINETEST_RICHED20 to keep windows
3971    * responsive and open for 30 seconds. This is useful for debugging.
3972    *
3973    * The message pump uses PeekMessage() to empty the queue and then sleeps for
3974    * 50ms before retrying the queue. */
3975   end = time(NULL) + 30;
3976   if (getenv( "WINETEST_RICHED20" )) {
3977     while (time(NULL) < end) {
3978       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3979         TranslateMessage(&msg);
3980         DispatchMessage(&msg);
3981       } else {
3982         Sleep(50);
3983       }
3984     }
3985   }
3986
3987   OleFlushClipboard();
3988   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
3989 }