richedit: Add tests for EM_FINDTEXT in 1.0 emulation, make them pass under Wine.
[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\n",
145      name, id, f->needle, f->start, f->end, f->flags, findloc);
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   memset(&ft, 0, sizeof(ft));
153   ft.chrg.cpMin = f->start;
154   ft.chrg.cpMax = f->end;
155   ft.lpstrText = f->needle;
156   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
157   ok(findloc == f->expected_loc,
158       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
159       name, id, f->needle, f->start, f->end, f->flags, findloc);
160   ok(ft.chrgText.cpMin == f->expected_loc,
161       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
162       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
163   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
164         : f->expected_loc + strlen(f->needle)),
165       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
166       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
167 }
168
169 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
170     int num_tests)
171 {
172   int i;
173
174   for (i = 0; i < num_tests; i++) {
175     if (find[i]._todo_wine) {
176       todo_wine {
177         check_EM_FINDTEXT(hwnd, name, &find[i], i);
178         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
179       }
180     } else {
181         check_EM_FINDTEXT(hwnd, name, &find[i], i);
182         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
183     }
184   }
185 }
186
187 static void test_EM_FINDTEXT(void)
188 {
189   HWND hwndRichEdit = new_richedit(NULL);
190
191   /* Empty rich edit control */
192   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
193       sizeof(find_tests)/sizeof(struct find_s));
194
195   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
196
197   /* Haystack text */
198   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
199       sizeof(find_tests2)/sizeof(struct find_s));
200
201   DestroyWindow(hwndRichEdit);
202 }
203
204 static const struct getline_s {
205   int line;
206   size_t buffer_len;
207   const char *text;
208 } gl[] = {
209   {0, 10, "foo bar\r"},
210   {1, 10, "\r"},
211   {2, 10, "bar\r"},
212   {3, 10, "\r"},
213
214   /* Buffer smaller than line length */
215   {0, 2, "foo bar\r"},
216   {0, 1, "foo bar\r"},
217   {0, 0, "foo bar\r"}
218 };
219
220 static void test_EM_GETLINE(void)
221 {
222   int i;
223   HWND hwndRichEdit = new_richedit(NULL);
224   static const int nBuf = 1024;
225   char dest[1024], origdest[1024];
226   const char text[] = "foo bar\n"
227       "\n"
228       "bar\n";
229
230   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
231
232   memset(origdest, 0xBB, nBuf);
233   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
234   {
235     int nCopied;
236     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
237     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
238     memset(dest, 0xBB, nBuf);
239     *(WORD *) dest = gl[i].buffer_len;
240
241     /* EM_GETLINE appends a "\r\0" to the end of the line
242      * nCopied counts up to and including the '\r' */
243     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
244     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
245        expected_nCopied);
246     /* two special cases since a parameter is passed via dest */
247     if (gl[i].buffer_len == 0)
248       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
249          "buffer_len=0\n");
250     else if (gl[i].buffer_len == 1)
251       ok(dest[0] == gl[i].text[0] && !dest[1] &&
252          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
253     else
254     {
255       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
256          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
257       ok(!strncmp(dest + expected_bytes_written, origdest
258                   + expected_bytes_written, nBuf - expected_bytes_written),
259          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
260     }
261   }
262
263   DestroyWindow(hwndRichEdit);
264 }
265
266 static void test_EM_LINELENGTH(void)
267 {
268   HWND hwndRichEdit = new_richedit(NULL);
269   const char * text =
270         "richedit1\r"
271         "richedit1\n"
272         "richedit1\r\n"
273         "richedit1";
274   int offset_test[10][2] = {
275         {0, 9},
276         {5, 9},
277         {10, 9},
278         {15, 9},
279         {20, 9},
280         {25, 9},
281         {30, 9},
282         {35, 9},
283         {40, 0},
284         {45, 0},
285   };
286   int i;
287   LRESULT result;
288
289   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
290
291   for (i = 0; i < 10; i++) {
292     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
293     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
294         offset_test[i][0], result, offset_test[i][1]);
295   }
296
297   DestroyWindow(hwndRichEdit);
298 }
299
300 static int get_scroll_pos_y(HWND hwnd)
301 {
302   POINT p = {-1, -1};
303   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
304   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
305   return p.y;
306 }
307
308 static void move_cursor(HWND hwnd, long charindex)
309 {
310   CHARRANGE cr;
311   cr.cpMax = charindex;
312   cr.cpMin = charindex;
313   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
314 }
315
316 static void line_scroll(HWND hwnd, int amount)
317 {
318   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
319 }
320
321 static void test_EM_SCROLLCARET(void)
322 {
323   int prevY, curY;
324   HWND hwndRichEdit = new_richedit(NULL);
325   const char text[] = "aa\n"
326       "this is a long line of text that should be longer than the "
327       "control's width\n"
328       "cc\n"
329       "dd\n"
330       "ee\n"
331       "ff\n"
332       "gg\n"
333       "hh\n";
334
335   /* Can't verify this */
336   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
337
338   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
339
340   /* Caret above visible window */
341   line_scroll(hwndRichEdit, 3);
342   prevY = get_scroll_pos_y(hwndRichEdit);
343   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
344   curY = get_scroll_pos_y(hwndRichEdit);
345   ok(prevY != curY, "%d == %d\n", prevY, curY);
346
347   /* Caret below visible window */
348   move_cursor(hwndRichEdit, sizeof(text) - 1);
349   line_scroll(hwndRichEdit, -3);
350   prevY = get_scroll_pos_y(hwndRichEdit);
351   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
352   curY = get_scroll_pos_y(hwndRichEdit);
353   ok(prevY != curY, "%d == %d\n", prevY, curY);
354
355   /* Caret in visible window */
356   move_cursor(hwndRichEdit, sizeof(text) - 2);
357   prevY = get_scroll_pos_y(hwndRichEdit);
358   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
359   curY = get_scroll_pos_y(hwndRichEdit);
360   ok(prevY == curY, "%d != %d\n", prevY, curY);
361
362   /* Caret still in visible window */
363   line_scroll(hwndRichEdit, -1);
364   prevY = get_scroll_pos_y(hwndRichEdit);
365   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
366   curY = get_scroll_pos_y(hwndRichEdit);
367   ok(prevY == curY, "%d != %d\n", prevY, curY);
368
369   DestroyWindow(hwndRichEdit);
370 }
371
372 static void test_EM_SETCHARFORMAT(void)
373 {
374   HWND hwndRichEdit = new_richedit(NULL);
375   CHARFORMAT2 cf2;
376   int rc = 0;
377
378   /* Invalid flags, CHARFORMAT2 structure blanked out */
379   memset(&cf2, 0, sizeof(cf2));
380   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
381              (LPARAM) &cf2);
382   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
383
384   /* A valid flag, CHARFORMAT2 structure blanked out */
385   memset(&cf2, 0, sizeof(cf2));
386   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
387              (LPARAM) &cf2);
388   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
389
390   /* A valid flag, CHARFORMAT2 structure blanked out */
391   memset(&cf2, 0, sizeof(cf2));
392   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
393              (LPARAM) &cf2);
394   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
395
396   /* A valid flag, CHARFORMAT2 structure blanked out */
397   memset(&cf2, 0, sizeof(cf2));
398   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
399              (LPARAM) &cf2);
400   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
401
402   /* A valid flag, CHARFORMAT2 structure blanked out */
403   memset(&cf2, 0, sizeof(cf2));
404   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
405              (LPARAM) &cf2);
406   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
407
408   /* Invalid flags, CHARFORMAT2 structure minimally filled */
409   memset(&cf2, 0, sizeof(cf2));
410   cf2.cbSize = sizeof(CHARFORMAT2);
411   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
412              (LPARAM) &cf2);
413   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
414
415   /* A valid flag, CHARFORMAT2 structure minimally filled */
416   memset(&cf2, 0, sizeof(cf2));
417   cf2.cbSize = sizeof(CHARFORMAT2);
418   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
419              (LPARAM) &cf2);
420   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
421
422   /* A valid flag, CHARFORMAT2 structure minimally filled */
423   memset(&cf2, 0, sizeof(cf2));
424   cf2.cbSize = sizeof(CHARFORMAT2);
425   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
426              (LPARAM) &cf2);
427   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
428
429   /* A valid flag, CHARFORMAT2 structure minimally filled */
430   memset(&cf2, 0, sizeof(cf2));
431   cf2.cbSize = sizeof(CHARFORMAT2);
432   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
433              (LPARAM) &cf2);
434   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
435
436   /* A valid flag, CHARFORMAT2 structure minimally filled */
437   memset(&cf2, 0, sizeof(cf2));
438   cf2.cbSize = sizeof(CHARFORMAT2);
439   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
440              (LPARAM) &cf2);
441   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
442
443   cf2.cbSize = sizeof(CHARFORMAT2);
444   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
445              (LPARAM) &cf2);
446
447   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
448   cf2.cbSize = sizeof(CHARFORMAT2);
449   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
450              (LPARAM) &cf2);
451   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
452   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
453
454   /* wParam==0 is default char format, does not set modify */
455   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
456   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
457   ok(rc == 0, "Text marked as modified, expected not modified!\n");
458   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
459   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
460   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
461   ok(rc == 0, "Text marked as modified, expected not modified!\n");
462
463   /* wParam==SCF_SELECTION sets modify if nonempty selection */
464   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
465   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
466   ok(rc == 0, "Text marked as modified, expected not modified!\n");
467   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
468   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
469   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
470   ok(rc == 0, "Text marked as modified, expected not modified!\n");
471
472   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
473   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
474   ok(rc == 0, "Text marked as modified, expected not modified!\n");
475   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
476   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
477   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
478   ok(rc == 0, "Text marked as modified, expected not modified!\n");
479   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
480   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
481   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
482   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
483   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
484
485   /* wParam==SCF_ALL sets modify regardless of whether text is present */
486   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
487   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
488   ok(rc == 0, "Text marked as modified, expected not modified!\n");
489   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
490   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
491   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
492   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
493
494   DestroyWindow(hwndRichEdit);
495 }
496
497 static void test_EM_SETTEXTMODE(void)
498 {
499   HWND hwndRichEdit = new_richedit(NULL);
500   CHARFORMAT2 cf2, cf2test;
501   CHARRANGE cr;
502   int rc = 0;
503
504   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
505   /*Insert text into the control*/
506
507   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
508
509   /*Attempt to change the control to plain text mode*/
510   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
511   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
512
513   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
514   If rich text is pasted, it should have the same formatting as the rest
515   of the text in the control*/
516
517   /*Italicize the text
518   *NOTE: If the default text was already italicized, the test will simply
519   reverse; in other words, it will copy a regular "wine" into a plain
520   text window that uses an italicized format*/
521   cf2.cbSize = sizeof(CHARFORMAT2);
522   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
523              (LPARAM) &cf2);
524
525   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
526   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
527
528   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
529   ok(rc == 0, "Text marked as modified, expected not modified!\n");
530
531   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
532   however, SCF_ALL has been implemented*/
533   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
534   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
535
536   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
537   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
538
539   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
540
541   /*Select the string "wine"*/
542   cr.cpMin = 0;
543   cr.cpMax = 4;
544   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
545
546   /*Copy the italicized "wine" to the clipboard*/
547   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
548
549   /*Reset the formatting to default*/
550   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
551   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
552   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
553
554   /*Clear the text in the control*/
555   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
556
557   /*Switch to Plain Text Mode*/
558   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
559   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
560
561   /*Input "wine" again in normal format*/
562   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
563
564   /*Paste the italicized "wine" into the control*/
565   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
566
567   /*Select a character from the first "wine" string*/
568   cr.cpMin = 2;
569   cr.cpMax = 3;
570   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
571
572   /*Retrieve its formatting*/
573   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
574               (LPARAM) &cf2);
575
576   /*Select a character from the second "wine" string*/
577   cr.cpMin = 5;
578   cr.cpMax = 6;
579   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
580
581   /*Retrieve its formatting*/
582   cf2test.cbSize = sizeof(CHARFORMAT2);
583   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
584                (LPARAM) &cf2test);
585
586   /*Compare the two formattings*/
587     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
588       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
589        cf2.dwEffects, cf2test.dwEffects);
590   /*Test TM_RICHTEXT by: switching back to Rich Text mode
591                          printing "wine" in the current format(normal)
592                          pasting "wine" from the clipboard(italicized)
593                          comparing the two formats(should differ)*/
594
595   /*Attempt to switch with text in control*/
596   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
597   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
598
599   /*Clear control*/
600   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
601
602   /*Switch into Rich Text mode*/
603   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
604   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
605
606   /*Print "wine" in normal formatting into the control*/
607   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
608
609   /*Paste italicized "wine" into the control*/
610   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
611
612   /*Select text from the first "wine" string*/
613   cr.cpMin = 1;
614   cr.cpMax = 3;
615   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
616
617   /*Retrieve its formatting*/
618   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
619                 (LPARAM) &cf2);
620
621   /*Select text from the second "wine" string*/
622   cr.cpMin = 6;
623   cr.cpMax = 7;
624   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
625
626   /*Retrieve its formatting*/
627   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
628                 (LPARAM) &cf2test);
629
630   /*Test that the two formattings are not the same*/
631   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
632       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
633       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
634
635   DestroyWindow(hwndRichEdit);
636 }
637
638 static void test_TM_PLAINTEXT(void)
639 {
640   /*Tests plain text properties*/
641
642   HWND hwndRichEdit = new_richedit(NULL);
643   CHARFORMAT2 cf2, cf2test;
644   CHARRANGE cr;
645   int rc = 0;
646
647   /*Switch to plain text mode*/
648
649   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
650   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
651
652   /*Fill control with text*/
653
654   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
655
656   /*Select some text and bold it*/
657
658   cr.cpMin = 10;
659   cr.cpMax = 20;
660   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
661   cf2.cbSize = sizeof(CHARFORMAT2);
662   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
663               (LPARAM) &cf2);
664
665   cf2.dwMask = CFM_BOLD | cf2.dwMask;
666   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
667
668   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
669   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
670
671   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
672   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
673
674   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
675   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676
677   /*Get the formatting of those characters*/
678
679   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
680
681   /*Get the formatting of some other characters*/
682   cf2test.cbSize = sizeof(CHARFORMAT2);
683   cr.cpMin = 21;
684   cr.cpMax = 30;
685   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
686   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
687
688   /*Test that they are the same as plain text allows only one formatting*/
689
690   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
691      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
692      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
693   
694   /*Fill the control with a "wine" string, which when inserted will be bold*/
695
696   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
697
698   /*Copy the bolded "wine" string*/
699
700   cr.cpMin = 0;
701   cr.cpMax = 4;
702   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
703   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
704
705   /*Swap back to rich text*/
706
707   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
708   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
709
710   /*Set the default formatting to bold italics*/
711
712   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
713   cf2.dwMask |= CFM_ITALIC;
714   cf2.dwEffects ^= CFE_ITALIC;
715   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
716   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
717
718   /*Set the text in the control to "wine", which will be bold and italicized*/
719
720   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
721
722   /*Paste the plain text "wine" string, which should take the insert
723    formatting, which at the moment is bold italics*/
724
725   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
726
727   /*Select the first "wine" string and retrieve its formatting*/
728
729   cr.cpMin = 1;
730   cr.cpMax = 3;
731   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
732   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
733
734   /*Select the second "wine" string and retrieve its formatting*/
735
736   cr.cpMin = 5;
737   cr.cpMax = 7;
738   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
739   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
740
741   /*Compare the two formattings. They should be the same.*/
742
743   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
744      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
745      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
746   DestroyWindow(hwndRichEdit);
747 }
748
749 static void test_WM_GETTEXT(void)
750 {
751     HWND hwndRichEdit = new_richedit(NULL);
752     static const char text[] = "Hello. My name is RichEdit!";
753     static const char text2[] = "Hello. My name is RichEdit!\r";
754     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
755     char buffer[1024] = {0};
756     int result;
757
758     /* Baseline test with normal-sized buffer */
759     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
760     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
761     ok(result == lstrlen(buffer),
762         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
763     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
764     result = strcmp(buffer,text);
765     ok(result == 0, 
766         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
767
768     /* Test for returned value of WM_GETTEXTLENGTH */
769     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
770     ok(result == lstrlen(text),
771         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
772         result, lstrlen(text));
773
774     /* Test for behavior in overflow case */
775     memset(buffer, 0, 1024);
776     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
777     ok(result == 0 ||
778        result == lstrlenA(text) - 1, /* XP, win2k3 */
779         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
780     result = strcmp(buffer,text);
781     if (result)
782         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
783     ok(result == 0,
784         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
785
786     /* Baseline test with normal-sized buffer and carriage return */
787     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
788     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
789     ok(result == lstrlen(buffer),
790         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
791     result = strcmp(buffer,text2_after);
792     ok(result == 0,
793         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
794
795     /* Test for returned value of WM_GETTEXTLENGTH */
796     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
797     ok(result == lstrlen(text2_after),
798         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
799         result, lstrlen(text2_after));
800
801     /* Test for behavior of CRLF conversion in case of overflow */
802     memset(buffer, 0, 1024);
803     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
804     ok(result == 0 ||
805        result == lstrlenA(text2) - 1, /* XP, win2k3 */
806         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
807     result = strcmp(buffer,text2);
808     if (result)
809         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
810     ok(result == 0,
811         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
812
813     DestroyWindow(hwndRichEdit);
814 }
815
816 static void test_EM_GETTEXTRANGE(void)
817 {
818     HWND hwndRichEdit = new_richedit(NULL);
819     const char * text1 = "foo bar\r\nfoo bar";
820     const char * text2 = "foo bar\rfoo bar";
821     const char * expect = "bar\rfoo";
822     char buffer[1024] = {0};
823     LRESULT result;
824     TEXTRANGEA textRange;
825
826     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
827
828     textRange.lpstrText = buffer;
829     textRange.chrg.cpMin = 4;
830     textRange.chrg.cpMax = 11;
831     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
832     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
833         result, strlen(expect));
834     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
835
836     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
837
838     textRange.lpstrText = buffer;
839     textRange.chrg.cpMin = 4;
840     textRange.chrg.cpMax = 11;
841     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
842     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
843         result, strlen(expect));
844     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
845
846     DestroyWindow(hwndRichEdit);
847 }
848
849 static void test_EM_GETSELTEXT(void)
850 {
851     HWND hwndRichEdit = new_richedit(NULL);
852     const char * text1 = "foo bar\r\nfoo bar";
853     const char * text2 = "foo bar\rfoo bar";
854     const char * expect = "bar\rfoo";
855     char buffer[1024] = {0};
856     LRESULT result;
857
858     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
859
860     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
861     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
862     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
863         result, strlen(expect));
864     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
865
866     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
867
868     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
869     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
870     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
871         result, strlen(expect));
872     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
873
874     DestroyWindow(hwndRichEdit);
875 }
876
877 /* FIXME: need to test unimplemented options and robustly test wparam */
878 static void test_EM_SETOPTIONS(void)
879 {
880     HWND hwndRichEdit = new_richedit(NULL);
881     static const char text[] = "Hello. My name is RichEdit!";
882     char buffer[1024] = {0};
883
884     /* NEGATIVE TESTING - NO OPTIONS SET */
885     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
886     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
887
888     /* testing no readonly by sending 'a' to the control*/
889     SetFocus(hwndRichEdit);
890     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
891     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
892     ok(buffer[0]=='a', 
893        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
894     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
895
896     /* READONLY - sending 'a' to the control */
897     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
898     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
899     SetFocus(hwndRichEdit);
900     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
901     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
902     ok(buffer[0]==text[0], 
903        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
904
905     DestroyWindow(hwndRichEdit);
906 }
907
908 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
909 {
910   CHARFORMAT2W text_format;
911   int link_present = 0;
912   text_format.cbSize = sizeof(text_format);
913   SendMessage(hwnd, EM_SETSEL, 0, 1);
914   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
915   link_present = text_format.dwEffects & CFE_LINK;
916   if (is_url) 
917   { /* control text is url; should get CFE_LINK */
918         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
919   }
920   else 
921   {
922     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
923   }
924 }
925
926 static HWND new_static_wnd(HWND parent) {
927   return new_window("Static", 0, parent);
928 }
929
930 static void test_EM_AUTOURLDETECT(void)
931 {
932   struct urls_s {
933     const char *text;
934     int is_url;
935   } urls[12] = {
936     {"winehq.org", 0},
937     {"http://www.winehq.org", 1},
938     {"http//winehq.org", 0},
939     {"ww.winehq.org", 0},
940     {"www.winehq.org", 1},
941     {"ftp://192.168.1.1", 1},
942     {"ftp//192.168.1.1", 0},
943     {"mailto:your@email.com", 1},    
944     {"prospero:prosperoserver", 1},
945     {"telnet:test", 1},
946     {"news:newserver", 1},
947     {"wais:waisserver", 1}  
948   };
949
950   int i;
951   int urlRet=-1;
952   HWND hwndRichEdit, parent;
953
954   parent = new_static_wnd(NULL);
955   hwndRichEdit = new_richedit(parent);
956   /* Try and pass EM_AUTOURLDETECT some test wParam values */
957   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
958   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
959   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
960   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
961   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
962   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
963   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
964   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
965   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
966   /* for each url, check the text to see if CFE_LINK effect is present */
967   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
968     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
969     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
970     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
971     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
972     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
973     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
974     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
975     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
976   }
977   DestroyWindow(hwndRichEdit);
978   DestroyWindow(parent);
979 }
980
981 static void test_EM_SCROLL(void)
982 {
983   int i, j;
984   int r; /* return value */
985   int expr; /* expected return value */
986   HWND hwndRichEdit = new_richedit(NULL);
987   int y_before, y_after; /* units of lines of text */
988
989   /* test a richedit box containing a single line of text */
990   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
991   expr = 0x00010000;
992   for (i = 0; i < 4; i++) {
993     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
994
995     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
996     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
997     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
998        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
999     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
1000        "(i == %d)\n", y_after, i);
1001   }
1002
1003   /*
1004    * test a richedit box that will scroll. There are two general
1005    * cases: the case without any long lines and the case with a long
1006    * line.
1007    */
1008   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1009     if (i == 0)
1010       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1011     else
1012       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1013                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1014                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1015                   "LONG LINE \nb\nc\nd\ne");
1016     for (j = 0; j < 12; j++) /* reset scroll position to top */
1017       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1018
1019     /* get first visible line */
1020     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1021     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1022
1023     /* get new current first visible line */
1024     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1025
1026     ok(((r & 0xffffff00) == 0x00010000) &&
1027        ((r & 0x000000ff) != 0x00000000),
1028        "EM_SCROLL page down didn't scroll by a small positive number of "
1029        "lines (r == 0x%08x)\n", r);
1030     ok(y_after > y_before, "EM_SCROLL page down not functioning "
1031        "(line %d scrolled to line %d\n", y_before, y_after);
1032
1033     y_before = y_after;
1034     
1035     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1036     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1037     ok(((r & 0xffffff00) == 0x0001ff00),
1038        "EM_SCROLL page up didn't scroll by a small negative number of lines "
1039        "(r == 0x%08x)\n", r);
1040     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1041        "%d scrolled to line %d\n", y_before, y_after);
1042     
1043     y_before = y_after;
1044
1045     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1046
1047     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1048
1049     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1050        "(r == 0x%08x)\n", r);
1051     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1052        "1 line (%d scrolled to %d)\n", y_before, y_after);
1053
1054     y_before = y_after;
1055
1056     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1057
1058     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1059
1060     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1061        "(r == 0x%08x)\n", r);
1062     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1063        "line (%d scrolled to %d)\n", y_before, y_after);
1064
1065     y_before = y_after;
1066
1067     r = SendMessage(hwndRichEdit, EM_SCROLL,
1068                     SB_LINEUP, 0); /* lineup beyond top */
1069
1070     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1071
1072     ok(r == 0x00010000,
1073        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1074     ok(y_before == y_after,
1075        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1076
1077     y_before = y_after;
1078
1079     r = SendMessage(hwndRichEdit, EM_SCROLL,
1080                     SB_PAGEUP, 0);/*page up beyond top */
1081
1082     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1083
1084     ok(r == 0x00010000,
1085        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1086     ok(y_before == y_after,
1087        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1088
1089     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1090       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1091     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1092     r = SendMessage(hwndRichEdit, EM_SCROLL,
1093                     SB_PAGEDOWN, 0); /* page down beyond bot */
1094     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1095
1096     ok(r == 0x00010000,
1097        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1098     ok(y_before == y_after,
1099        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1100        y_before, y_after);
1101
1102     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1103     SendMessage(hwndRichEdit, EM_SCROLL,
1104                 SB_LINEDOWN, 0); /* line down beyond bot */
1105     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1106     
1107     ok(r == 0x00010000,
1108        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1109     ok(y_before == y_after,
1110        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1111        y_before, y_after);
1112   }
1113   DestroyWindow(hwndRichEdit);
1114 }
1115
1116 static void test_EM_SETUNDOLIMIT(void)
1117 {
1118   /* cases we test for:
1119    * default behaviour - limiting at 100 undo's 
1120    * undo disabled - setting a limit of 0
1121    * undo limited -  undo limit set to some to some number, like 2
1122    * bad input - sending a negative number should default to 100 undo's */
1123  
1124   HWND hwndRichEdit = new_richedit(NULL);
1125   CHARRANGE cr;
1126   int i;
1127   int result;
1128   
1129   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1130   cr.cpMin = 0;
1131   cr.cpMax = 1;
1132   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1133     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1134       also, multiple pastes don't combine like WM_CHAR would */
1135   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1136
1137   /* first case - check the default */
1138   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1139   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1140     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1141   for (i=0; i<100; i++) /* Undo 100 of them */
1142     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1143   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1144      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1145
1146   /* second case - cannot undo */
1147   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1148   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
1149   SendMessage(hwndRichEdit,
1150               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1151   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1152      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1153
1154   /* third case - set it to an arbitrary number */
1155   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1156   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
1157   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1158   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1159   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1160   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1161   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1162      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1163   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1164   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1165      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1166   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1167   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1168      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1169   
1170   /* fourth case - setting negative numbers should default to 100 undos */
1171   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1172   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1173   ok (result == 100, 
1174       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1175       
1176   DestroyWindow(hwndRichEdit);
1177 }
1178
1179 static void test_ES_PASSWORD(void)
1180 {
1181   /* This isn't hugely testable, so we're just going to run it through its paces */
1182
1183   HWND hwndRichEdit = new_richedit(NULL);
1184   WCHAR result;
1185
1186   /* First, check the default of a regular control */
1187   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1188   ok (result == 0,
1189         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1190
1191   /* Now, set it to something normal */
1192   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1193   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1194   ok (result == 120,
1195         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1196
1197   /* Now, set it to something odd */
1198   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1199   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1200   ok (result == 1234,
1201         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1202   DestroyWindow(hwndRichEdit);
1203 }
1204
1205 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1206                                          LPBYTE pbBuff,
1207                                          LONG cb,
1208                                          LONG *pcb)
1209 {
1210   char** str = (char**)dwCookie;
1211   *pcb = cb;
1212   if (*pcb > 0) {
1213     memcpy(*str, pbBuff, *pcb);
1214     *str += *pcb;
1215   }
1216   return 0;
1217 }
1218
1219 static void test_WM_SETTEXT()
1220 {
1221   HWND hwndRichEdit = new_richedit(NULL);
1222   const char * TestItem1 = "TestSomeText";
1223   const char * TestItem2 = "TestSomeText\r";
1224   const char * TestItem2_after = "TestSomeText\r\n";
1225   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1226   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1227   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1228   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1229   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1230   const char * TestItem5_after = "TestSomeText TestSomeText";
1231   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1232   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1233   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1234   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1235
1236   char buf[1024] = {0};
1237   LRESULT result;
1238   EDITSTREAM es;
1239   char * p;
1240
1241   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1242      any solitary \r to be converted to \r\n on return. Properly paired
1243      \r\n are not affected. It also shows that the special sequence \r\r\n
1244      gets converted to a single space.
1245    */
1246
1247 #define TEST_SETTEXT(a, b) \
1248   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1249   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1250   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1251   ok (result == lstrlen(buf), \
1252         "WM_GETTEXT returned %ld instead of expected %u\n", \
1253         result, lstrlen(buf)); \
1254   result = strcmp(b, buf); \
1255   ok(result == 0, \
1256         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1257
1258   TEST_SETTEXT(TestItem1, TestItem1)
1259   TEST_SETTEXT(TestItem2, TestItem2_after)
1260   TEST_SETTEXT(TestItem3, TestItem3_after)
1261   TEST_SETTEXT(TestItem3_after, TestItem3_after)
1262   TEST_SETTEXT(TestItem4, TestItem4_after)
1263   TEST_SETTEXT(TestItem5, TestItem5_after)
1264   TEST_SETTEXT(TestItem6, TestItem6_after)
1265   TEST_SETTEXT(TestItem7, TestItem7_after)
1266
1267   /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1268   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1269   p = buf;
1270   es.dwCookie = (DWORD_PTR)&p;
1271   es.dwError = 0;
1272   es.pfnCallback = test_WM_SETTEXT_esCallback;
1273   memset(buf, 0, sizeof(buf));
1274   SendMessage(hwndRichEdit, EM_STREAMOUT,
1275               (WPARAM)(SF_RTF), (LPARAM)&es);
1276   trace("EM_STREAMOUT produced: \n%s\n", buf);
1277   TEST_SETTEXT(buf, TestItem1)
1278
1279 #undef TEST_SETTEXT
1280   DestroyWindow(hwndRichEdit);
1281 }
1282
1283 static void test_EM_STREAMOUT(void)
1284 {
1285   HWND hwndRichEdit = new_richedit(NULL);
1286   int r;
1287   EDITSTREAM es;
1288   char buf[1024] = {0};
1289   char * p;
1290
1291   const char * TestItem1 = "TestSomeText";
1292   const char * TestItem2 = "TestSomeText\r";
1293   const char * TestItem3 = "TestSomeText\r\n";
1294
1295   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1296   p = buf;
1297   es.dwCookie = (DWORD_PTR)&p;
1298   es.dwError = 0;
1299   es.pfnCallback = test_WM_SETTEXT_esCallback;
1300   memset(buf, 0, sizeof(buf));
1301   SendMessage(hwndRichEdit, EM_STREAMOUT,
1302               (WPARAM)(SF_TEXT), (LPARAM)&es);
1303   r = strlen(buf);
1304   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
1305   ok(strcmp(buf, TestItem1) == 0,
1306         "streamed text different, got %s\n", buf);
1307
1308   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
1309   p = buf;
1310   es.dwCookie = (DWORD_PTR)&p;
1311   es.dwError = 0;
1312   es.pfnCallback = test_WM_SETTEXT_esCallback;
1313   memset(buf, 0, sizeof(buf));
1314   SendMessage(hwndRichEdit, EM_STREAMOUT,
1315               (WPARAM)(SF_TEXT), (LPARAM)&es);
1316   r = strlen(buf);
1317   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
1318   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1319   ok(strcmp(buf, TestItem3) == 0,
1320         "streamed text different from, got %s\n", buf);
1321   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1322   p = buf;
1323   es.dwCookie = (DWORD_PTR)&p;
1324   es.dwError = 0;
1325   es.pfnCallback = test_WM_SETTEXT_esCallback;
1326   memset(buf, 0, sizeof(buf));
1327   SendMessage(hwndRichEdit, EM_STREAMOUT,
1328               (WPARAM)(SF_TEXT), (LPARAM)&es);
1329   r = strlen(buf);
1330   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1331   ok(strcmp(buf, TestItem3) == 0,
1332         "streamed text different, got %s\n", buf);
1333
1334   DestroyWindow(hwndRichEdit);
1335 }
1336
1337 static void test_EM_SETTEXTEX(void)
1338 {
1339   HWND hwndRichEdit = new_richedit(NULL);
1340   SETTEXTEX setText;
1341   GETTEXTEX getText;
1342   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1343                        'S', 'o', 'm', 'e', 
1344                        'T', 'e', 'x', 't', 0}; 
1345   WCHAR TestItem2[] = {'T', 'e', 's', 't',
1346                        'S', 'o', 'm', 'e',
1347                        'T', 'e', 'x', 't',
1348                       '\r', 0};
1349   const char * TestItem2_after = "TestSomeText\r\n";
1350   WCHAR TestItem3[] = {'T', 'e', 's', 't',
1351                        'S', 'o', 'm', 'e',
1352                        'T', 'e', 'x', 't',
1353                       '\r','\n','\r','\n', 0};
1354   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1355                        'S', 'o', 'm', 'e',
1356                        'T', 'e', 'x', 't',
1357                        '\n','\n', 0};
1358   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1359                        'S', 'o', 'm', 'e',
1360                        'T', 'e', 'x', 't',
1361                        '\r','\r', 0};
1362   WCHAR TestItem4[] = {'T', 'e', 's', 't',
1363                        'S', 'o', 'm', 'e',
1364                        'T', 'e', 'x', 't',
1365                       '\r','\r','\n','\r',
1366                       '\n', 0};
1367   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1368                        'S', 'o', 'm', 'e',
1369                        'T', 'e', 'x', 't',
1370                        ' ','\r', 0};
1371 #define MAX_BUF_LEN 1024
1372   WCHAR buf[MAX_BUF_LEN];
1373   char * p;
1374   int result;
1375   CHARRANGE cr;
1376   EDITSTREAM es;
1377
1378   setText.codepage = 1200;  /* no constant for unicode */
1379   getText.codepage = 1200;  /* no constant for unicode */
1380   getText.cb = MAX_BUF_LEN;
1381   getText.flags = GT_DEFAULT;
1382   getText.lpDefaultChar = NULL;
1383   getText.lpUsedDefChar = NULL;
1384
1385   setText.flags = 0;
1386   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1387   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1388   ok(lstrcmpW(buf, TestItem1) == 0,
1389       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1390
1391   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1392      convert \r to \r\n on return
1393    */
1394   setText.codepage = 1200;  /* no constant for unicode */
1395   getText.codepage = 1200;  /* no constant for unicode */
1396   getText.cb = MAX_BUF_LEN;
1397   getText.flags = GT_DEFAULT;
1398   getText.lpDefaultChar = NULL;
1399   getText.lpUsedDefChar = NULL;
1400   setText.flags = 0;
1401   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1402   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1403   ok(lstrcmpW(buf, TestItem2) == 0,
1404       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1405
1406   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1407   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1408   ok(strcmp((const char *)buf, TestItem2_after) == 0,
1409       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1410
1411   /* Baseline test for just-enough buffer space for string */
1412   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1413   getText.codepage = 1200;  /* no constant for unicode */
1414   getText.flags = GT_DEFAULT;
1415   getText.lpDefaultChar = NULL;
1416   getText.lpUsedDefChar = NULL;
1417   memset(buf, 0, MAX_BUF_LEN);
1418   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1419   ok(lstrcmpW(buf, TestItem2) == 0,
1420       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1421
1422   /* When there is enough space for one character, but not both, of the CRLF
1423      pair at the end of the string, the CR is not copied at all. That is,
1424      the caller must not see CRLF pairs truncated to CR at the end of the
1425      string.
1426    */
1427   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1428   getText.codepage = 1200;  /* no constant for unicode */
1429   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
1430   getText.lpDefaultChar = NULL;
1431   getText.lpUsedDefChar = NULL;
1432   memset(buf, 0, MAX_BUF_LEN);
1433   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1434   ok(lstrcmpW(buf, TestItem1) == 0,
1435       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1436
1437
1438   /* \r\n pairs get changed into \r */
1439   setText.codepage = 1200;  /* no constant for unicode */
1440   getText.codepage = 1200;  /* no constant for unicode */
1441   getText.cb = MAX_BUF_LEN;
1442   getText.flags = GT_DEFAULT;
1443   getText.lpDefaultChar = NULL;
1444   getText.lpUsedDefChar = NULL;
1445   setText.flags = 0;
1446   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1447   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1448   ok(lstrcmpW(buf, TestItem3_after) == 0,
1449       "EM_SETTEXTEX did not convert properly\n");
1450
1451   /* \n also gets changed to \r */
1452   setText.codepage = 1200;  /* no constant for unicode */
1453   getText.codepage = 1200;  /* no constant for unicode */
1454   getText.cb = MAX_BUF_LEN;
1455   getText.flags = GT_DEFAULT;
1456   getText.lpDefaultChar = NULL;
1457   getText.lpUsedDefChar = NULL;
1458   setText.flags = 0;
1459   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1460   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1461   ok(lstrcmpW(buf, TestItem3_after) == 0,
1462       "EM_SETTEXTEX did not convert properly\n");
1463
1464   /* \r\r\n gets changed into single space */
1465   setText.codepage = 1200;  /* no constant for unicode */
1466   getText.codepage = 1200;  /* no constant for unicode */
1467   getText.cb = MAX_BUF_LEN;
1468   getText.flags = GT_DEFAULT;
1469   getText.lpDefaultChar = NULL;
1470   getText.lpUsedDefChar = NULL;
1471   setText.flags = 0;
1472   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1473   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1474   ok(lstrcmpW(buf, TestItem4_after) == 0,
1475       "EM_SETTEXTEX did not convert properly\n");
1476
1477   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1478                        (WPARAM)&setText, (LPARAM) NULL);
1479   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1480   
1481   ok (result == 1, 
1482       "EM_SETTEXTEX returned %d, instead of 1\n",result);
1483   ok(lstrlenW(buf) == 0,
1484       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1485   
1486   /* put some text back */
1487   setText.flags = 0;
1488   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1489   /* select some text */
1490   cr.cpMax = 1;
1491   cr.cpMin = 3;
1492   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1493   /* replace current selection */
1494   setText.flags = ST_SELECTION;
1495   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1496                        (WPARAM)&setText, (LPARAM) NULL);
1497   ok(result == 0,
1498       "EM_SETTEXTEX with NULL lParam to replace selection"
1499       " with no text should return 0. Got %i\n",
1500       result);
1501   
1502   /* put some text back */
1503   setText.flags = 0;
1504   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1505   /* select some text */
1506   cr.cpMax = 1;
1507   cr.cpMin = 3;
1508   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1509   /* replace current selection */
1510   setText.flags = ST_SELECTION;
1511   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1512                        (WPARAM)&setText, (LPARAM) TestItem1);
1513   /* get text */
1514   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1515   ok(result == lstrlenW(TestItem1),
1516       "EM_SETTEXTEX with NULL lParam to replace selection"
1517       " with no text should return 0. Got %i\n",
1518       result);
1519   ok(lstrlenW(buf) == 22,
1520       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1521       lstrlenW(buf) );
1522
1523   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1524   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1525   p = (char *)buf;
1526   es.dwCookie = (DWORD_PTR)&p;
1527   es.dwError = 0;
1528   es.pfnCallback = test_WM_SETTEXT_esCallback;
1529   memset(buf, 0, sizeof(buf));
1530   SendMessage(hwndRichEdit, EM_STREAMOUT,
1531               (WPARAM)(SF_RTF), (LPARAM)&es);
1532   trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1533
1534   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1535   getText.codepage = 1200;  /* no constant for unicode */
1536   getText.cb = MAX_BUF_LEN;
1537   getText.flags = GT_DEFAULT;
1538   getText.lpDefaultChar = NULL;
1539   getText.lpUsedDefChar = NULL;
1540
1541   setText.flags = 0;
1542   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1543   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1544   ok(lstrcmpW(buf, TestItem1) == 0,
1545       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1546
1547
1548   DestroyWindow(hwndRichEdit);
1549 }
1550
1551 static void test_EM_LIMITTEXT(void)
1552 {
1553   int ret;
1554
1555   HWND hwndRichEdit = new_richedit(NULL);
1556
1557   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1558    * about setting the length to -1 for multiline edit controls doesn't happen.
1559    */
1560
1561   /* Don't check default gettextlimit case. That's done in other tests */
1562
1563   /* Set textlimit to 100 */
1564   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1565   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1566   ok (ret == 100,
1567       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1568
1569   /* Set textlimit to 0 */
1570   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1571   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1572   ok (ret == 65536,
1573       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1574
1575   /* Set textlimit to -1 */
1576   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1577   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1578   ok (ret == -1,
1579       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1580
1581   /* Set textlimit to -2 */
1582   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1583   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1584   ok (ret == -2,
1585       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1586
1587   DestroyWindow (hwndRichEdit);
1588 }
1589
1590
1591 static void test_EM_EXLIMITTEXT(void)
1592 {
1593   int i, selBegin, selEnd, len1, len2;
1594   int result;
1595   char text[1024 + 1];
1596   char buffer[1024 + 1];
1597   int textlimit = 0; /* multiple of 100 */
1598   HWND hwndRichEdit = new_richedit(NULL);
1599   
1600   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1601   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1602   
1603   textlimit = 256000;
1604   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1605   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1606   /* set higher */
1607   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1608   
1609   textlimit = 1000;
1610   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1611   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1612   /* set lower */
1613   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1614  
1615   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1616   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1617   /* default for WParam = 0 */
1618   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1619  
1620   textlimit = sizeof(text)-1;
1621   memset(text, 'W', textlimit);
1622   text[sizeof(text)-1] = 0;
1623   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1624   /* maxed out text */
1625   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1626   
1627   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1628   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1629   len1 = selEnd - selBegin;
1630   
1631   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1632   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1633   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1634   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1635   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1636   len2 = selEnd - selBegin;
1637   
1638   ok(len1 != len2,
1639     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1640     len1,len2,i);
1641   
1642   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1643   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1644   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1645   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1646   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1647   len1 = selEnd - selBegin;
1648   
1649   ok(len1 != len2,
1650     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1651     len1,len2,i);
1652   
1653   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1654   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1655   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1656   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1657   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1658   len2 = selEnd - selBegin;
1659   
1660   ok(len1 == len2, 
1661     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1662     len1,len2,i);
1663
1664   /* set text up to the limit, select all the text, then add a char */
1665   textlimit = 5;
1666   memset(text, 'W', textlimit);
1667   text[textlimit] = 0;
1668   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1669   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1670   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1671   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1672   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1673   result = strcmp(buffer, "A");
1674   ok(0 == result, "got string = \"%s\"\n", buffer);
1675
1676   /* WM_SETTEXT not limited */
1677   textlimit = 10;
1678   memset(text, 'W', textlimit);
1679   text[textlimit] = 0;
1680   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1681   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1682   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1683   i = strlen(buffer);
1684   ok(10 == i, "expected 10 chars\n");
1685   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1686   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1687
1688   /* try inserting more text at end */
1689   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1690   ok(0 == i, "WM_CHAR wasn't processed\n");
1691   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1692   i = strlen(buffer);
1693   ok(10 == i, "expected 10 chars, got %i\n", i);
1694   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1695   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1696
1697   /* try inserting text at beginning */
1698   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1699   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1700   ok(0 == i, "WM_CHAR wasn't processed\n");
1701   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1702   i = strlen(buffer);
1703   ok(10 == i, "expected 10 chars, got %i\n", i);
1704   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1705   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1706
1707   /* WM_CHAR is limited */
1708   textlimit = 1;
1709   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1710   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1711   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1712   ok(0 == i, "WM_CHAR wasn't processed\n");
1713   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1714   ok(0 == i, "WM_CHAR wasn't processed\n");
1715   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1716   i = strlen(buffer);
1717   ok(1 == i, "expected 1 chars, got %i instead\n", i);
1718
1719   DestroyWindow(hwndRichEdit);
1720 }
1721
1722 static void test_EM_GETLIMITTEXT(void)
1723 {
1724   int i;
1725   HWND hwndRichEdit = new_richedit(NULL);
1726
1727   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1728   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1729
1730   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1731   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1732   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1733
1734   DestroyWindow(hwndRichEdit);
1735 }
1736
1737 static void test_WM_SETFONT(void)
1738 {
1739   /* There is no invalid input or error conditions for this function.
1740    * NULL wParam and lParam just fall back to their default values 
1741    * It should be noted that even if you use a gibberish name for your fonts
1742    * here, it will still work because the name is stored. They will display as
1743    * System, but will report their name to be whatever they were created as */
1744   
1745   HWND hwndRichEdit = new_richedit(NULL);
1746   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1747     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1748     FF_DONTCARE, "Marlett");
1749   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1750     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1751     FF_DONTCARE, "MS Sans Serif");
1752   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1753     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1754     FF_DONTCARE, "Courier");
1755   LOGFONTA sentLogFont;
1756   CHARFORMAT2A returnedCF2A;
1757   
1758   returnedCF2A.cbSize = sizeof(returnedCF2A);
1759   
1760   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1761   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1762   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1763
1764   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1765   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1766     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1767     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1768
1769   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1770   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1771   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1772   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1773     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1774     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1775     
1776   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1777   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1778   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1779   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1780     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1781     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1782    
1783   /* This last test is special since we send in NULL. We clear the variables
1784    * and just compare to "System" instead of the sent in font name. */
1785   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1786   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1787   returnedCF2A.cbSize = sizeof(returnedCF2A);
1788   
1789   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1790   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1791   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1792   ok (!strcmp("System",returnedCF2A.szFaceName),
1793     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1794   
1795   DestroyWindow(hwndRichEdit);
1796 }
1797
1798
1799 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1800                                          LPBYTE pbBuff,
1801                                          LONG cb,
1802                                          LONG *pcb)
1803 {
1804   const char** str = (const char**)dwCookie;
1805   int size = strlen(*str);
1806   if(size > 3)  /* let's make it piecemeal for fun */
1807     size = 3;
1808   *pcb = cb;
1809   if (*pcb > size) {
1810     *pcb = size;
1811   }
1812   if (*pcb > 0) {
1813     memcpy(pbBuff, *str, *pcb);
1814     *str += *pcb;
1815   }
1816   return 0;
1817 }
1818
1819 static void test_EM_GETMODIFY(void)
1820 {
1821   HWND hwndRichEdit = new_richedit(NULL);
1822   LRESULT result;
1823   SETTEXTEX setText;
1824   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1825                        'S', 'o', 'm', 'e', 
1826                        'T', 'e', 'x', 't', 0}; 
1827   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1828                        'S', 'o', 'm', 'e', 
1829                        'O', 't', 'h', 'e', 'r',
1830                        'T', 'e', 'x', 't', 0}; 
1831   const char* streamText = "hello world";
1832   CHARFORMAT2 cf2;
1833   PARAFORMAT2 pf2;
1834   EDITSTREAM es;
1835   
1836   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1837     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1838     FF_DONTCARE, "Courier");
1839   
1840   setText.codepage = 1200;  /* no constant for unicode */
1841   setText.flags = ST_KEEPUNDO;
1842   
1843
1844   /* modify flag shouldn't be set when richedit is first created */
1845   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1846   ok (result == 0, 
1847       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1848   
1849   /* setting modify flag should actually set it */
1850   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1851   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1852   ok (result != 0, 
1853       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1854   
1855   /* clearing modify flag should actually clear it */
1856   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1857   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1858   ok (result == 0, 
1859       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1860  
1861   /* setting font doesn't change modify flag */
1862   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1863   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1864   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1865   ok (result == 0,
1866       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1867
1868   /* setting text should set modify flag */
1869   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1870   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1871   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1872   ok (result != 0,
1873       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1874   
1875   /* undo previous text doesn't reset modify flag */
1876   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1877   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1878   ok (result != 0,
1879       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1880   
1881   /* set text with no flag to keep undo stack should not set modify flag */
1882   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1883   setText.flags = 0;
1884   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1885   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1886   ok (result == 0,
1887       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1888   
1889   /* WM_SETTEXT doesn't modify */
1890   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1891   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1892   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1893   ok (result == 0,
1894       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1895   
1896   /* clear the text */
1897   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1898   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1899   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1900   ok (result == 0,
1901       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1902   
1903   /* replace text */
1904   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1905   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1906   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1907   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1908   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1909   ok (result != 0,
1910       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1911   
1912   /* copy/paste text 1 */
1913   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1914   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1915   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1916   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1917   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1918   ok (result != 0,
1919       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1920   
1921   /* copy/paste text 2 */
1922   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1923   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1924   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1925   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1926   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1927   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1928   ok (result != 0,
1929       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1930   
1931   /* press char */
1932   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1933   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1934   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1935   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1936   ok (result != 0,
1937       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1938
1939   /* press del */
1940   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1941   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1942   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1943   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1944   ok (result != 0,
1945       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1946   
1947   /* set char format */
1948   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1949   cf2.cbSize = sizeof(CHARFORMAT2);
1950   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1951              (LPARAM) &cf2);
1952   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1953   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1954   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1955   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1956   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1957   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1958   ok (result != 0,
1959       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1960   
1961   /* set para format */
1962   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1963   pf2.cbSize = sizeof(PARAFORMAT2);
1964   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1965              (LPARAM) &pf2);
1966   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1967   pf2.wAlignment = PFA_RIGHT;
1968   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1969   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1970   ok (result == 0,
1971       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1972
1973   /* EM_STREAM */
1974   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1975   es.dwCookie = (DWORD_PTR)&streamText;
1976   es.dwError = 0;
1977   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1978   SendMessage(hwndRichEdit, EM_STREAMIN, 
1979               (WPARAM)(SF_TEXT), (LPARAM)&es);
1980   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1981   ok (result != 0,
1982       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1983
1984   DestroyWindow(hwndRichEdit);
1985 }
1986
1987 struct exsetsel_s {
1988   long min;
1989   long max;
1990   long expected_retval;
1991   int expected_getsel_start;
1992   int expected_getsel_end;
1993   int _exsetsel_todo_wine;
1994   int _getsel_todo_wine;
1995 };
1996
1997 const struct exsetsel_s exsetsel_tests[] = {
1998   /* sanity tests */
1999   {5, 10, 10, 5, 10, 0, 0},
2000   {15, 17, 17, 15, 17, 0, 0},
2001   /* test cpMax > strlen() */
2002   {0, 100, 18, 0, 18, 0, 1},
2003   /* test cpMin == cpMax */
2004   {5, 5, 5, 5, 5, 0, 0},
2005   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2006   {-1, 0, 5, 5, 5, 0, 0},
2007   {-1, 17, 5, 5, 5, 0, 0},
2008   {-1, 18, 5, 5, 5, 0, 0},
2009   /* test cpMin < 0 && cpMax < 0 */
2010   {-1, -1, 17, 17, 17, 0, 0},
2011   {-4, -5, 17, 17, 17, 0, 0},
2012   /* test cMin >=0 && cpMax < 0 (bug 6814) */
2013   {0, -1, 18, 0, 18, 0, 1},
2014   {17, -5, 18, 17, 18, 0, 1},
2015   {18, -3, 17, 17, 17, 0, 0},
2016   /* test if cpMin > cpMax */
2017   {15, 19, 18, 15, 18, 0, 1},
2018   {19, 15, 18, 15, 18, 0, 1}
2019 };
2020
2021 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2022     CHARRANGE cr;
2023     long result;
2024     int start, end;
2025
2026     cr.cpMin = setsel->min;
2027     cr.cpMax = setsel->max;
2028     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2029
2030     if (setsel->_exsetsel_todo_wine) {
2031         todo_wine {
2032             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2033         }
2034     } else {
2035         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2036     }
2037
2038     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2039
2040     if (setsel->_getsel_todo_wine) {
2041         todo_wine {
2042             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);
2043         }
2044     } else {
2045         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);
2046     }
2047 }
2048
2049 static void test_EM_EXSETSEL(void)
2050 {
2051     HWND hwndRichEdit = new_richedit(NULL);
2052     int i;
2053     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2054
2055     /* sending some text to the window */
2056     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2057     /*                                                 01234567890123456*/
2058     /*                                                          10      */
2059
2060     for (i = 0; i < num_tests; i++) {
2061         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2062     }
2063
2064     DestroyWindow(hwndRichEdit);
2065 }
2066
2067 static void test_EM_REPLACESEL(int redraw)
2068 {
2069     HWND hwndRichEdit = new_richedit(NULL);
2070     char buffer[1024] = {0};
2071     int r;
2072     GETTEXTEX getText;
2073     CHARRANGE cr;
2074
2075     /* sending some text to the window */
2076     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2077     /*                                                 01234567890123456*/
2078     /*                                                          10      */
2079
2080     /* FIXME add more tests */
2081     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2082     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2083     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2084     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2085     r = strcmp(buffer, "testing");
2086     ok(0 == r, "expected %d, got %d\n", 0, r);
2087
2088     DestroyWindow(hwndRichEdit);
2089
2090     hwndRichEdit = new_richedit(NULL);
2091
2092     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2093     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2094
2095     /* Test behavior with carriage returns and newlines */
2096     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2097     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2098     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2099     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2100     r = strcmp(buffer, "RichEdit1");
2101     ok(0 == r, "expected %d, got %d\n", 0, r);
2102     getText.cb = 1024;
2103     getText.codepage = CP_ACP;
2104     getText.flags = GT_DEFAULT;
2105     getText.lpDefaultChar = NULL;
2106     getText.lpUsedDefChar = NULL;
2107     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2108     ok(strcmp(buffer, "RichEdit1") == 0,
2109       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2110
2111     /* Test number of lines reported after EM_REPLACESEL */
2112     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2113     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2114
2115     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2116     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2117     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2118     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2119     r = strcmp(buffer, "RichEdit1\r\n");
2120     ok(0 == r, "expected %d, got %d\n", 0, r);
2121     getText.cb = 1024;
2122     getText.codepage = CP_ACP;
2123     getText.flags = GT_DEFAULT;
2124     getText.lpDefaultChar = NULL;
2125     getText.lpUsedDefChar = NULL;
2126     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2127     ok(strcmp(buffer, "RichEdit1\r") == 0,
2128       "EM_GETTEXTEX returned incorrect string\n");
2129
2130     /* Test number of lines reported after EM_REPLACESEL */
2131     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2132     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2133
2134     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2135        EM_REPLACESEL. The general rule seems to be that Win98's riched20
2136        returns the number of characters *inserted* into the control (after
2137        required conversions), but WinXP's riched20 returns the number of
2138        characters interpreted from the original lParam. Wine's builtin riched20
2139        implements the WinXP behavior.
2140      */
2141     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2142     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2143     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2144         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2145
2146     /* Test number of lines reported after EM_REPLACESEL */
2147     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2148     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2149
2150     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2151     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2152     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2153     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2154
2155     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2156     r = strcmp(buffer, "RichEdit1\r\n");
2157     ok(0 == r, "expected %d, got %d\n", 0, r);
2158     getText.cb = 1024;
2159     getText.codepage = CP_ACP;
2160     getText.flags = GT_DEFAULT;
2161     getText.lpDefaultChar = NULL;
2162     getText.lpUsedDefChar = NULL;
2163     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2164     ok(strcmp(buffer, "RichEdit1\r") == 0,
2165       "EM_GETTEXTEX returned incorrect string\n");
2166
2167     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2168     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2169     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2170     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2171
2172     /* The following tests show that richedit should handle the special \r\r\n
2173        sequence by turning it into a single space on insertion. However,
2174        EM_REPLACESEL on WinXP returns the number of characters in the original
2175        string.
2176      */
2177
2178     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2179     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2180     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2181     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2182     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2183     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2184     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2185
2186     /* Test the actual string */
2187     getText.cb = 1024;
2188     getText.codepage = CP_ACP;
2189     getText.flags = GT_DEFAULT;
2190     getText.lpDefaultChar = NULL;
2191     getText.lpUsedDefChar = NULL;
2192     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2193     ok(strcmp(buffer, "\r\r") == 0,
2194       "EM_GETTEXTEX returned incorrect string\n");
2195
2196     /* Test number of lines reported after EM_REPLACESEL */
2197     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2198     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2199
2200     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2201     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2202     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2203         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2204     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2205     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2206     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2207     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2208
2209     /* Test the actual string */
2210     getText.cb = 1024;
2211     getText.codepage = CP_ACP;
2212     getText.flags = GT_DEFAULT;
2213     getText.lpDefaultChar = NULL;
2214     getText.lpUsedDefChar = NULL;
2215     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2216     ok(strcmp(buffer, " ") == 0,
2217       "EM_GETTEXTEX returned incorrect string\n");
2218
2219     /* Test number of lines reported after EM_REPLACESEL */
2220     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2221     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2222
2223     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2224     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2225     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2226         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2227     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2228     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2229     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2230     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2231
2232     /* Test the actual string */
2233     getText.cb = 1024;
2234     getText.codepage = CP_ACP;
2235     getText.flags = GT_DEFAULT;
2236     getText.lpDefaultChar = NULL;
2237     getText.lpUsedDefChar = NULL;
2238     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2239     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2240       "EM_GETTEXTEX returned incorrect string\n");
2241
2242     /* Test number of lines reported after EM_REPLACESEL */
2243     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2244     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2245
2246     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2247     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2248     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2249         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2250     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2251     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2252     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2253     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2254
2255     /* Test the actual string */
2256     getText.cb = 1024;
2257     getText.codepage = CP_ACP;
2258     getText.flags = GT_DEFAULT;
2259     getText.lpDefaultChar = NULL;
2260     getText.lpUsedDefChar = NULL;
2261     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2262     ok(strcmp(buffer, " \r") == 0,
2263       "EM_GETTEXTEX returned incorrect string\n");
2264
2265     /* Test number of lines reported after EM_REPLACESEL */
2266     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2267     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2268
2269     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2270     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2271     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2272         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2273     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2274     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2275     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2276     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2277
2278     /* Test the actual string */
2279     getText.cb = 1024;
2280     getText.codepage = CP_ACP;
2281     getText.flags = GT_DEFAULT;
2282     getText.lpDefaultChar = NULL;
2283     getText.lpUsedDefChar = NULL;
2284     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2285     ok(strcmp(buffer, " \r\r") == 0,
2286       "EM_GETTEXTEX returned incorrect string\n");
2287
2288     /* Test number of lines reported after EM_REPLACESEL */
2289     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2290     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2291
2292     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2293     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2294     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2295         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2296     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2297     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2298     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2299     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2300
2301     /* Test the actual string */
2302     getText.cb = 1024;
2303     getText.codepage = CP_ACP;
2304     getText.flags = GT_DEFAULT;
2305     getText.lpDefaultChar = NULL;
2306     getText.lpUsedDefChar = NULL;
2307     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2308     ok(strcmp(buffer, "\rX\r\r\r") == 0,
2309       "EM_GETTEXTEX returned incorrect string\n");
2310
2311     /* Test number of lines reported after EM_REPLACESEL */
2312     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2313     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2314
2315     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2316     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2317     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2318     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2319     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2320     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2321     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2322
2323     /* Test the actual string */
2324     getText.cb = 1024;
2325     getText.codepage = CP_ACP;
2326     getText.flags = GT_DEFAULT;
2327     getText.lpDefaultChar = NULL;
2328     getText.lpUsedDefChar = NULL;
2329     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2330     ok(strcmp(buffer, "\r\r") == 0,
2331       "EM_GETTEXTEX returned incorrect string\n");
2332
2333     /* Test number of lines reported after EM_REPLACESEL */
2334     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2335     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2336
2337     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2338     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2339     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2340         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2341     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2342     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2343     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2344     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2345
2346     /* Test the actual string */
2347     getText.cb = 1024;
2348     getText.codepage = CP_ACP;
2349     getText.flags = GT_DEFAULT;
2350     getText.lpDefaultChar = NULL;
2351     getText.lpUsedDefChar = NULL;
2352     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2353     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2354       "EM_GETTEXTEX returned incorrect string\n");
2355
2356     /* Test number of lines reported after EM_REPLACESEL */
2357     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2358     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2359
2360     DestroyWindow(hwndRichEdit);
2361 }
2362
2363 static void test_WM_PASTE(void)
2364 {
2365     MSG msg;
2366     int result;
2367     char buffer[1024] = {0};
2368     char key_info[][3] =
2369     {
2370         /* VirtualKey, ScanCode, WM_CHAR code */
2371         {'C', 0x2e,  3},        /* Ctrl-C */
2372         {'X', 0x2d, 24},        /* Ctrl-X */
2373         {'V', 0x2f, 22},        /* Ctrl-V */
2374         {'Z', 0x2c, 26},        /* Ctrl-Z */
2375         {'Y', 0x15, 25},        /* Ctrl-Y */
2376     };
2377     const char* text1 = "testing paste\r";
2378     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2379     const char* text1_after = "testing paste\r\n";
2380     const char* text2 = "testing paste\r\rtesting paste";
2381     const char* text2_after = "testing paste\r\n\r\ntesting paste";
2382     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2383     HWND hwndRichEdit = new_richedit(NULL);
2384
2385     /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2386        messages, probably because it inspects the keyboard state itself.
2387        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2388      */
2389 #define SEND_CTRL_KEY(hwnd, k) \
2390     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2391     keybd_event(k[0], k[1], 0, 0);\
2392     keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2393     keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2394     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2395         TranslateMessage(&msg); \
2396         DispatchMessage(&msg); \
2397     }
2398
2399 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2400 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2401 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2402 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2403 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2404
2405     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2406     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2407
2408     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2409     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2410     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2411     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2412     /* Pasted text should be visible at this step */
2413     result = strcmp(text1_step1, buffer);
2414     ok(result == 0,
2415         "test paste: strcmp = %i\n", result);
2416     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2417     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2418     /* Text should be the same as before (except for \r -> \r\n conversion) */
2419     result = strcmp(text1_after, buffer);
2420     ok(result == 0,
2421         "test paste: strcmp = %i\n", result);
2422
2423     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2424     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2425     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2426     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2427     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2428     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2429     /* Pasted text should be visible at this step */
2430     result = strcmp(text3, buffer);
2431     ok(result == 0,
2432         "test paste: strcmp = %i\n", result);
2433     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2434     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2435     /* Text should be the same as before (except for \r -> \r\n conversion) */
2436     result = strcmp(text2_after, buffer);
2437     ok(result == 0,
2438         "test paste: strcmp = %i\n", result);
2439     SEND_CTRL_Y(hwndRichEdit)   /* Redo */
2440     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2441     /* Text should revert to post-paste state */
2442     result = strcmp(buffer,text3);
2443     ok(result == 0,
2444         "test paste: strcmp = %i\n", result);
2445
2446     DestroyWindow(hwndRichEdit);
2447 }
2448
2449 static void test_EM_FORMATRANGE(void)
2450 {
2451   int r;
2452   FORMATRANGE fr;
2453   HDC hdc;
2454   HWND hwndRichEdit = new_richedit(NULL);
2455
2456   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2457
2458   hdc = GetDC(hwndRichEdit);
2459   ok(hdc != NULL, "Could not get HDC\n");
2460
2461   fr.hdc = fr.hdcTarget = hdc;
2462   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2463   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2464   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2465   fr.chrg.cpMin = 0;
2466   fr.chrg.cpMax = 20;
2467
2468   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2469   todo_wine {
2470     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2471   }
2472
2473   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2474   todo_wine {
2475     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2476   }
2477
2478   fr.chrg.cpMin = 0;
2479   fr.chrg.cpMax = 10;
2480
2481   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2482   todo_wine {
2483     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2484   }
2485
2486   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2487   todo_wine {
2488     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2489   }
2490
2491   DestroyWindow(hwndRichEdit);
2492 }
2493
2494 static int nCallbackCount = 0;
2495
2496 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2497                                  LONG cb, LONG* pcb)
2498 {
2499   const char text[] = {'t','e','s','t'};
2500
2501   if (sizeof(text) <= cb)
2502   {
2503     if ((int)dwCookie != nCallbackCount)
2504     {
2505       *pcb = 0;
2506       return 0;
2507     }
2508
2509     memcpy (pbBuff, text, sizeof(text));
2510     *pcb = sizeof(text);
2511
2512     nCallbackCount++;
2513
2514     return 0;
2515   }
2516   else
2517     return 1; /* indicates callback failed */
2518 }
2519
2520 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2521                                          LPBYTE pbBuff,
2522                                          LONG cb,
2523                                          LONG *pcb)
2524 {
2525   const char** str = (const char**)dwCookie;
2526   int size = strlen(*str);
2527   *pcb = cb;
2528   if (*pcb > size) {
2529     *pcb = size;
2530   }
2531   if (*pcb > 0) {
2532     memcpy(pbBuff, *str, *pcb);
2533     *str += *pcb;
2534   }
2535   return 0;
2536 }
2537
2538
2539 static void test_EM_STREAMIN(void)
2540 {
2541   HWND hwndRichEdit = new_richedit(NULL);
2542   LRESULT result;
2543   EDITSTREAM es;
2544   char buffer[1024] = {0};
2545
2546   const char * streamText0 = "{\\rtf1 TestSomeText}";
2547   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2548   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2549
2550   const char * streamText1 =
2551   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2552   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2553   "}\r\n";
2554
2555   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2556   const char * streamText2 =
2557     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2558     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2559     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2560     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2561     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2562     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2563     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2564
2565   const char * streamText3 = "RichEdit1";
2566
2567   /* Minimal test without \par at the end */
2568   es.dwCookie = (DWORD_PTR)&streamText0;
2569   es.dwError = 0;
2570   es.pfnCallback = test_EM_STREAMIN_esCallback;
2571   SendMessage(hwndRichEdit, EM_STREAMIN,
2572               (WPARAM)(SF_RTF), (LPARAM)&es);
2573
2574   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2575   ok (result  == 12,
2576       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2577   result = strcmp (buffer,"TestSomeText");
2578   ok (result  == 0,
2579       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2580   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
2581
2582   /* Native richedit 2.0 ignores last \par */
2583   es.dwCookie = (DWORD_PTR)&streamText0a;
2584   es.dwError = 0;
2585   es.pfnCallback = test_EM_STREAMIN_esCallback;
2586   SendMessage(hwndRichEdit, EM_STREAMIN,
2587               (WPARAM)(SF_RTF), (LPARAM)&es);
2588
2589   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2590   ok (result  == 12,
2591       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2592   result = strcmp (buffer,"TestSomeText");
2593   ok (result  == 0,
2594       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2595   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
2596
2597   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2598   es.dwCookie = (DWORD_PTR)&streamText0b;
2599   es.dwError = 0;
2600   es.pfnCallback = test_EM_STREAMIN_esCallback;
2601   SendMessage(hwndRichEdit, EM_STREAMIN,
2602               (WPARAM)(SF_RTF), (LPARAM)&es);
2603
2604   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2605   ok (result  == 14,
2606       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2607   result = strcmp (buffer,"TestSomeText\r\n");
2608   ok (result  == 0,
2609       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2610   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
2611
2612   es.dwCookie = (DWORD_PTR)&streamText1;
2613   es.dwError = 0;
2614   es.pfnCallback = test_EM_STREAMIN_esCallback;
2615   SendMessage(hwndRichEdit, EM_STREAMIN,
2616               (WPARAM)(SF_RTF), (LPARAM)&es);
2617
2618   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2619   ok (result  == 12,
2620       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2621   result = strcmp (buffer,"TestSomeText");
2622   ok (result  == 0,
2623       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2624   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
2625
2626   es.dwCookie = (DWORD_PTR)&streamText2;
2627   es.dwError = 0;
2628   SendMessage(hwndRichEdit, EM_STREAMIN,
2629               (WPARAM)(SF_RTF), (LPARAM)&es);
2630
2631   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2632   ok (result  == 0,
2633       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2634   ok (strlen(buffer)  == 0,
2635       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2636   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
2637
2638   es.dwCookie = (DWORD_PTR)&streamText3;
2639   es.dwError = 0;
2640   SendMessage(hwndRichEdit, EM_STREAMIN,
2641               (WPARAM)(SF_RTF), (LPARAM)&es);
2642
2643   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2644   ok (result  == 0,
2645       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
2646   ok (strlen(buffer)  == 0,
2647       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2648   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
2649
2650   DestroyWindow(hwndRichEdit);
2651 }
2652
2653 static void test_EM_StreamIn_Undo(void)
2654 {
2655   /* The purpose of this test is to determine when a EM_StreamIn should be
2656    * undoable. This is important because WM_PASTE currently uses StreamIn and
2657    * pasting should always be undoable but streaming isn't always.
2658    *
2659    * cases to test:
2660    * StreamIn plain text without SFF_SELECTION.
2661    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2662    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2663    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2664    * Feel free to add tests for other text modes or StreamIn things.
2665    */
2666
2667
2668   HWND hwndRichEdit = new_richedit(NULL);
2669   LRESULT result;
2670   EDITSTREAM es;
2671   char buffer[1024] = {0};
2672   const char randomtext[] = "Some text";
2673
2674   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2675
2676   /* StreamIn, no SFF_SELECTION */
2677   es.dwCookie = nCallbackCount;
2678   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2679   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2680   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2681   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2682   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2683   result = strcmp (buffer,"test");
2684   ok (result  == 0,
2685       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2686
2687   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2688   ok (result == FALSE,
2689       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2690
2691   /* StreamIn, SFF_SELECTION, but nothing selected */
2692   es.dwCookie = nCallbackCount;
2693   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2694   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2695   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2696   SendMessage(hwndRichEdit, EM_STREAMIN,
2697               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2698   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2699   result = strcmp (buffer,"testSome text");
2700   ok (result  == 0,
2701       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2702
2703   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2704   ok (result == TRUE,
2705      "EM_STREAMIN with SFF_SELECTION but no selection set "
2706       "should create an undo\n");
2707
2708   /* StreamIn, SFF_SELECTION, with a selection */
2709   es.dwCookie = nCallbackCount;
2710   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2711   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2712   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2713   SendMessage(hwndRichEdit, EM_STREAMIN,
2714               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2715   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2716   result = strcmp (buffer,"Sometesttext");
2717   ok (result  == 0,
2718       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2719
2720   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2721   ok (result == TRUE,
2722       "EM_STREAMIN with SFF_SELECTION and selection set "
2723       "should create an undo\n");
2724
2725 }
2726
2727 static BOOL is_em_settextex_supported(HWND hwnd)
2728 {
2729     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2730     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2731 }
2732
2733 static void test_unicode_conversions(void)
2734 {
2735     static const WCHAR tW[] = {'t',0};
2736     static const WCHAR teW[] = {'t','e',0};
2737     static const WCHAR textW[] = {'t','e','s','t',0};
2738     static const char textA[] = "test";
2739     char bufA[64];
2740     WCHAR bufW[64];
2741     HWND hwnd;
2742     int is_win9x, em_settextex_supported, ret;
2743
2744     is_win9x = GetVersion() & 0x80000000;
2745
2746 #define set_textA(hwnd, wm_set_text, txt) \
2747     do { \
2748         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2749         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2750         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2751         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2752         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2753     } while(0)
2754 #define expect_textA(hwnd, wm_get_text, txt) \
2755     do { \
2756         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2757         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2758         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2759         memset(bufA, 0xAA, sizeof(bufA)); \
2760         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2761         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2762         ret = lstrcmpA(bufA, txt); \
2763         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2764     } while(0)
2765
2766 #define set_textW(hwnd, wm_set_text, txt) \
2767     do { \
2768         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2769         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2770         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2771         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2772         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2773     } while(0)
2774 #define expect_textW(hwnd, wm_get_text, txt) \
2775     do { \
2776         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2777         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2778         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2779         memset(bufW, 0xAA, sizeof(bufW)); \
2780         if (is_win9x) \
2781         { \
2782             assert(wm_get_text == EM_GETTEXTEX); \
2783             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2784             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2785         } \
2786         else \
2787         { \
2788             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2789             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2790         } \
2791         ret = lstrcmpW(bufW, txt); \
2792         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2793     } while(0)
2794 #define expect_empty(hwnd, wm_get_text) \
2795     do { \
2796         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2797         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2798         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2799         memset(bufA, 0xAA, sizeof(bufA)); \
2800         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2801         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2802         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2803     } while(0)
2804
2805     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2806                            0, 0, 200, 60, 0, 0, 0, 0);
2807     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2808
2809     ret = IsWindowUnicode(hwnd);
2810     if (is_win9x)
2811         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2812     else
2813         ok(ret, "RichEdit20W should be unicode under NT\n");
2814
2815     /* EM_SETTEXTEX is supported starting from version 3.0 */
2816     em_settextex_supported = is_em_settextex_supported(hwnd);
2817     trace("EM_SETTEXTEX is %ssupported on this platform\n",
2818           em_settextex_supported ? "" : "NOT ");
2819
2820     expect_empty(hwnd, WM_GETTEXT);
2821     expect_empty(hwnd, EM_GETTEXTEX);
2822
2823     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2824     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2825     expect_textA(hwnd, WM_GETTEXT, "t");
2826     expect_textA(hwnd, EM_GETTEXTEX, "t");
2827     expect_textW(hwnd, EM_GETTEXTEX, tW);
2828
2829     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2830     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2831     expect_textA(hwnd, WM_GETTEXT, "te");
2832     expect_textA(hwnd, EM_GETTEXTEX, "te");
2833     expect_textW(hwnd, EM_GETTEXTEX, teW);
2834
2835     set_textA(hwnd, WM_SETTEXT, NULL);
2836     expect_empty(hwnd, WM_GETTEXT);
2837     expect_empty(hwnd, EM_GETTEXTEX);
2838
2839     if (is_win9x)
2840         set_textA(hwnd, WM_SETTEXT, textW);
2841     else
2842         set_textA(hwnd, WM_SETTEXT, textA);
2843     expect_textA(hwnd, WM_GETTEXT, textA);
2844     expect_textA(hwnd, EM_GETTEXTEX, textA);
2845     expect_textW(hwnd, EM_GETTEXTEX, textW);
2846
2847     if (em_settextex_supported)
2848     {
2849         set_textA(hwnd, EM_SETTEXTEX, textA);
2850         expect_textA(hwnd, WM_GETTEXT, textA);
2851         expect_textA(hwnd, EM_GETTEXTEX, textA);
2852         expect_textW(hwnd, EM_GETTEXTEX, textW);
2853     }
2854
2855     if (!is_win9x)
2856     {
2857         set_textW(hwnd, WM_SETTEXT, textW);
2858         expect_textW(hwnd, WM_GETTEXT, textW);
2859         expect_textA(hwnd, WM_GETTEXT, textA);
2860         expect_textW(hwnd, EM_GETTEXTEX, textW);
2861         expect_textA(hwnd, EM_GETTEXTEX, textA);
2862
2863         if (em_settextex_supported)
2864         {
2865             set_textW(hwnd, EM_SETTEXTEX, textW);
2866             expect_textW(hwnd, WM_GETTEXT, textW);
2867             expect_textA(hwnd, WM_GETTEXT, textA);
2868             expect_textW(hwnd, EM_GETTEXTEX, textW);
2869             expect_textA(hwnd, EM_GETTEXTEX, textA);
2870         }
2871     }
2872     DestroyWindow(hwnd);
2873
2874     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2875                            0, 0, 200, 60, 0, 0, 0, 0);
2876     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2877
2878     ret = IsWindowUnicode(hwnd);
2879     ok(!ret, "RichEdit20A should NOT be unicode\n");
2880
2881     set_textA(hwnd, WM_SETTEXT, textA);
2882     expect_textA(hwnd, WM_GETTEXT, textA);
2883     expect_textA(hwnd, EM_GETTEXTEX, textA);
2884     expect_textW(hwnd, EM_GETTEXTEX, textW);
2885
2886     if (em_settextex_supported)
2887     {
2888         set_textA(hwnd, EM_SETTEXTEX, textA);
2889         expect_textA(hwnd, WM_GETTEXT, textA);
2890         expect_textA(hwnd, EM_GETTEXTEX, textA);
2891         expect_textW(hwnd, EM_GETTEXTEX, textW);
2892     }
2893
2894     if (!is_win9x)
2895     {
2896         set_textW(hwnd, WM_SETTEXT, textW);
2897         expect_textW(hwnd, WM_GETTEXT, textW);
2898         expect_textA(hwnd, WM_GETTEXT, textA);
2899         expect_textW(hwnd, EM_GETTEXTEX, textW);
2900         expect_textA(hwnd, EM_GETTEXTEX, textA);
2901
2902         if (em_settextex_supported)
2903         {
2904             set_textW(hwnd, EM_SETTEXTEX, textW);
2905             expect_textW(hwnd, WM_GETTEXT, textW);
2906             expect_textA(hwnd, WM_GETTEXT, textA);
2907             expect_textW(hwnd, EM_GETTEXTEX, textW);
2908             expect_textA(hwnd, EM_GETTEXTEX, textA);
2909         }
2910     }
2911     DestroyWindow(hwnd);
2912 }
2913
2914 static void test_WM_CHAR(void)
2915 {
2916     HWND hwnd;
2917     int ret;
2918     const char * char_list = "abc\rabc\r";
2919     const char * expected_content_single = "abcabc";
2920     const char * expected_content_multi = "abc\r\nabc\r\n";
2921     char buffer[64] = {0};
2922     const char * p;
2923
2924     /* single-line control must IGNORE carriage returns */
2925     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2926                            0, 0, 200, 60, 0, 0, 0, 0);
2927     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2928
2929     p = char_list;
2930     while (*p != '\0') {
2931         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2932         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2933         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2934         SendMessageA(hwnd, WM_KEYUP, *p, 1);
2935         p++;
2936     }
2937
2938     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2939     ret = strcmp(buffer, expected_content_single);
2940     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2941
2942     DestroyWindow(hwnd);
2943
2944     /* multi-line control inserts CR normally */
2945     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2946                            0, 0, 200, 60, 0, 0, 0, 0);
2947     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2948
2949     p = char_list;
2950     while (*p != '\0') {
2951         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2952         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2953         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2954         SendMessageA(hwnd, WM_KEYUP, *p, 1);
2955         p++;
2956     }
2957
2958     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2959     ret = strcmp(buffer, expected_content_multi);
2960     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2961
2962     DestroyWindow(hwnd);
2963 }
2964
2965 static void test_EM_GETTEXTLENGTHEX(void)
2966 {
2967     HWND hwnd;
2968     GETTEXTLENGTHEX gtl;
2969     int ret;
2970     const char * base_string = "base string";
2971     const char * test_string = "a\nb\n\n\r\n";
2972     const char * test_string_after = "a";
2973     const char * test_string_2 = "a\rtest\rstring";
2974     char buffer[64] = {0};
2975
2976     /* single line */
2977     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2978                            0, 0, 200, 60, 0, 0, 0, 0);
2979     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2980
2981     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2982     gtl.codepage = CP_ACP;
2983     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2984     ok(ret == 0, "ret %d\n",ret);
2985
2986     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2987     gtl.codepage = CP_ACP;
2988     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2989     ok(ret == 0, "ret %d\n",ret);
2990
2991     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
2992
2993     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2994     gtl.codepage = CP_ACP;
2995     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2996     ok(ret == strlen(base_string), "ret %d\n",ret);
2997
2998     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2999     gtl.codepage = CP_ACP;
3000     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3001     ok(ret == strlen(base_string), "ret %d\n",ret);
3002
3003     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3004
3005     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3006     gtl.codepage = CP_ACP;
3007     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3008     ok(ret == 1, "ret %d\n",ret);
3009
3010     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3011     gtl.codepage = CP_ACP;
3012     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3013     ok(ret == 1, "ret %d\n",ret);
3014
3015     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3016     ret = strcmp(buffer, test_string_after);
3017     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3018
3019     DestroyWindow(hwnd);
3020
3021     /* multi line */
3022     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3023                            0, 0, 200, 60, 0, 0, 0, 0);
3024     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3025
3026     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3027     gtl.codepage = CP_ACP;
3028     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3029     ok(ret == 0, "ret %d\n",ret);
3030
3031     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3032     gtl.codepage = CP_ACP;
3033     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3034     ok(ret == 0, "ret %d\n",ret);
3035
3036     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3037
3038     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3039     gtl.codepage = CP_ACP;
3040     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3041     ok(ret == strlen(base_string), "ret %d\n",ret);
3042
3043     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3044     gtl.codepage = CP_ACP;
3045     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3046     ok(ret == strlen(base_string), "ret %d\n",ret);
3047
3048     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3049
3050     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3051     gtl.codepage = CP_ACP;
3052     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3053     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3054
3055     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3056     gtl.codepage = CP_ACP;
3057     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3058     ok(ret == strlen(test_string_2), "ret %d\n",ret);
3059
3060     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3061
3062     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3063     gtl.codepage = CP_ACP;
3064     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3065     ok(ret == 10, "ret %d\n",ret);
3066
3067     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3068     gtl.codepage = CP_ACP;
3069     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3070     ok(ret == 6, "ret %d\n",ret);
3071
3072     DestroyWindow(hwnd);
3073 }
3074
3075
3076 /* globals that parent and child access when checking event masks & notifications */
3077 static HWND eventMaskEditHwnd = 0;
3078 static int queriedEventMask;
3079 static int watchForEventMask = 0;
3080
3081 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3082 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3083 {
3084     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3085     {
3086       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3087     }
3088     return DefWindowProcA(hwnd, message, wParam, lParam);
3089 }
3090
3091 /* test event masks in combination with WM_COMMAND */
3092 static void test_eventMask(void)
3093 {
3094     HWND parent;
3095     int ret;
3096     WNDCLASSA cls;
3097     const char text[] = "foo bar\n";
3098     int eventMask;
3099
3100     /* register class to capture WM_COMMAND */
3101     cls.style = 0;
3102     cls.lpfnWndProc = ParentMsgCheckProcA;
3103     cls.cbClsExtra = 0;
3104     cls.cbWndExtra = 0;
3105     cls.hInstance = GetModuleHandleA(0);
3106     cls.hIcon = 0;
3107     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3108     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3109     cls.lpszMenuName = NULL;
3110     cls.lpszClassName = "EventMaskParentClass";
3111     if(!RegisterClassA(&cls)) assert(0);
3112
3113     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3114                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3115     ok (parent != 0, "Failed to create parent window\n");
3116
3117     eventMaskEditHwnd = new_richedit(parent);
3118     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3119
3120     eventMask = ENM_CHANGE | ENM_UPDATE;
3121     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3122     ok(ret == ENM_NONE, "wrong event mask\n");
3123     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3124     ok(ret == eventMask, "failed to set event mask\n");
3125
3126     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3127     queriedEventMask = 0;  /* initialize to something other than we expect */
3128     watchForEventMask = EN_CHANGE;
3129     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3130     ok(ret == TRUE, "failed to set text\n");
3131     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3132        notification in response to WM_SETTEXT */
3133     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3134             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3135
3136 }
3137
3138 static int received_WM_NOTIFY = 0;
3139 static int modify_at_WM_NOTIFY = 0;
3140 static HWND hwndRichedit_WM_NOTIFY;
3141
3142 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3143 {
3144     if(message == WM_NOTIFY)
3145     {
3146       received_WM_NOTIFY = 1;
3147       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3148     }
3149     return DefWindowProcA(hwnd, message, wParam, lParam);
3150 }
3151
3152 static void test_WM_NOTIFY(void)
3153 {
3154     HWND parent;
3155     WNDCLASSA cls;
3156     CHARFORMAT2 cf2;
3157
3158     /* register class to capture WM_NOTIFY */
3159     cls.style = 0;
3160     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3161     cls.cbClsExtra = 0;
3162     cls.cbWndExtra = 0;
3163     cls.hInstance = GetModuleHandleA(0);
3164     cls.hIcon = 0;
3165     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3166     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3167     cls.lpszMenuName = NULL;
3168     cls.lpszClassName = "WM_NOTIFY_ParentClass";
3169     if(!RegisterClassA(&cls)) assert(0);
3170
3171     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3172                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3173     ok (parent != 0, "Failed to create parent window\n");
3174
3175     hwndRichedit_WM_NOTIFY = new_richedit(parent);
3176     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3177
3178     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3179
3180     /* Notifications for selection change should only be sent when selection
3181        actually changes. EM_SETCHARFORMAT is one message that calls
3182        ME_CommitUndo, which should check whether message should be sent */
3183     received_WM_NOTIFY = 0;
3184     cf2.cbSize = sizeof(CHARFORMAT2);
3185     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3186              (LPARAM) &cf2);
3187     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3188     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3189     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3190     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3191
3192     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3193        already at 0. */
3194     received_WM_NOTIFY = 0;
3195     modify_at_WM_NOTIFY = 0;
3196     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3197     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3198     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3199
3200     received_WM_NOTIFY = 0;
3201     modify_at_WM_NOTIFY = 0;
3202     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3203     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3204
3205     received_WM_NOTIFY = 0;
3206     modify_at_WM_NOTIFY = 0;
3207     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3208     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3209     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3210
3211     DestroyWindow(hwndRichedit_WM_NOTIFY);
3212     DestroyWindow(parent);
3213 }
3214
3215 START_TEST( editor )
3216 {
3217   MSG msg;
3218   time_t end;
3219
3220   /* Must explicitly LoadLibrary(). The test has no references to functions in
3221    * RICHED20.DLL, so the linker doesn't actually link to it. */
3222   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3223   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3224   test_WM_CHAR();
3225   test_EM_FINDTEXT();
3226   test_EM_GETLINE();
3227   test_EM_SCROLLCARET();
3228   test_EM_SCROLL();
3229   test_WM_SETTEXT();
3230   test_EM_LINELENGTH();
3231   test_EM_SETCHARFORMAT();
3232   test_EM_SETTEXTMODE();
3233   test_TM_PLAINTEXT();
3234   test_EM_SETOPTIONS();
3235   test_WM_GETTEXT();
3236   test_EM_GETTEXTRANGE();
3237   test_EM_GETSELTEXT();
3238   test_EM_AUTOURLDETECT();
3239   test_EM_SETUNDOLIMIT();
3240   test_ES_PASSWORD();
3241   test_EM_SETTEXTEX();
3242   test_EM_LIMITTEXT();
3243   test_EM_EXLIMITTEXT();
3244   test_EM_GETLIMITTEXT();
3245   test_WM_SETFONT();
3246   test_EM_GETMODIFY();
3247   test_EM_EXSETSEL();
3248   test_WM_PASTE();
3249   test_EM_STREAMIN();
3250   test_EM_STREAMOUT();
3251   test_EM_StreamIn_Undo();
3252   test_EM_FORMATRANGE();
3253   test_unicode_conversions();
3254   test_EM_GETTEXTLENGTHEX();
3255   test_EM_REPLACESEL(1);
3256   test_EM_REPLACESEL(0);
3257   test_WM_NOTIFY();
3258   test_eventMask();
3259
3260   /* Set the environment variable WINETEST_RICHED20 to keep windows
3261    * responsive and open for 30 seconds. This is useful for debugging.
3262    *
3263    * The message pump uses PeekMessage() to empty the queue and then sleeps for
3264    * 50ms before retrying the queue. */
3265   end = time(NULL) + 30;
3266   if (getenv( "WINETEST_RICHED20" )) {
3267     while (time(NULL) < end) {
3268       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3269         TranslateMessage(&msg);
3270         DispatchMessage(&msg);
3271       } else {
3272         Sleep(50);
3273       }
3274     }
3275   }
3276
3277   OleFlushClipboard();
3278   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
3279 }