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