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