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