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