wintrust: Use a helper function to get a signer's cert info from a message.
[wine] / dlls / riched20 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
34
35 static HMODULE hmoduleRichEdit;
36
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38   HWND hwnd;
39   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41                       hmoduleRichEdit, NULL);
42   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
43   return hwnd;
44 }
45
46 static HWND new_richedit(HWND parent) {
47   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
48 }
49
50 static const char haystack[] = "WINEWine wineWine wine WineWine";
51                              /* ^0        ^10       ^20       ^30 */
52
53 struct find_s {
54   int start;
55   int end;
56   const char *needle;
57   int flags;
58   int expected_loc;
59   int _todo_wine;
60 };
61
62
63 struct find_s find_tests[] = {
64   /* Find in empty text */
65   {0, -1, "foo", FR_DOWN, -1, 0},
66   {0, -1, "foo", 0, -1, 0},
67   {0, -1, "", FR_DOWN, -1, 0},
68   {20, 5, "foo", FR_DOWN, -1, 0},
69   {5, 20, "foo", FR_DOWN, -1, 0}
70 };
71
72 struct find_s find_tests2[] = {
73   /* No-result find */
74   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
75   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
76
77   /* Subsequent finds */
78   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
79   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
80   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
81   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
82
83   /* Find backwards */
84   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
85   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
86   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
87
88   /* Case-insensitive */
89   {1, 31, "wInE", FR_DOWN, 4, 0},
90   {1, 31, "Wine", FR_DOWN, 4, 0},
91
92   /* High-to-low ranges */
93   {20, 5, "Wine", FR_DOWN, -1, 0},
94   {2, 1, "Wine", FR_DOWN, -1, 0},
95   {30, 29, "Wine", FR_DOWN, -1, 0},
96   {20, 5, "Wine", 0, 13, 0},
97
98   /* Find nothing */
99   {5, 10, "", FR_DOWN, -1, 0},
100   {10, 5, "", FR_DOWN, -1, 0},
101   {0, -1, "", FR_DOWN, -1, 0},
102   {10, 5, "", 0, -1, 0},
103
104   /* Whole-word search */
105   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
106   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
107   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
108   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
109   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
110   {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
111   {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
112   
113   /* Bad ranges */
114   {5, 200, "XXX", FR_DOWN, -1, 0},
115   {-20, 20, "Wine", FR_DOWN, -1, 0},
116   {-20, 20, "Wine", FR_DOWN, -1, 0},
117   {-15, -20, "Wine", FR_DOWN, -1, 0},
118   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
119
120   /* Check the case noted in bug 4479 where matches at end aren't recognized */
121   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
122   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
124   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
126
127   /* The backwards case of bug 4479; bounds look right
128    * Fails because backward find is wrong */
129   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
130   {0, 20, "WINE", FR_MATCHCASE, -1, 0}
131 };
132
133 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
134   int findloc;
135   FINDTEXT ft;
136   memset(&ft, 0, sizeof(ft));
137   ft.chrg.cpMin = f->start;
138   ft.chrg.cpMax = f->end;
139   ft.lpstrText = f->needle;
140   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
141   ok(findloc == f->expected_loc,
142      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
143      name, id, f->needle, f->start, f->end, f->flags, findloc);
144 }
145
146 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
147     int id) {
148   int findloc;
149   FINDTEXTEX ft;
150   memset(&ft, 0, sizeof(ft));
151   ft.chrg.cpMin = f->start;
152   ft.chrg.cpMax = f->end;
153   ft.lpstrText = f->needle;
154   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
155   ok(findloc == f->expected_loc,
156       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
157       name, id, f->needle, f->start, f->end, f->flags, findloc);
158   ok(ft.chrgText.cpMin == f->expected_loc,
159       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
160       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
161   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
162         : f->expected_loc + strlen(f->needle)),
163       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
164       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
165 }
166
167 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
168     int num_tests)
169 {
170   int i;
171
172   for (i = 0; i < num_tests; i++) {
173     if (find[i]._todo_wine) {
174       todo_wine {
175         check_EM_FINDTEXT(hwnd, name, &find[i], i);
176         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
177       }
178     } else {
179         check_EM_FINDTEXT(hwnd, name, &find[i], i);
180         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
181     }
182   }
183 }
184
185 static void test_EM_FINDTEXT(void)
186 {
187   HWND hwndRichEdit = new_richedit(NULL);
188
189   /* Empty rich edit control */
190   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
191       sizeof(find_tests)/sizeof(struct find_s));
192
193   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
194
195   /* Haystack text */
196   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
197       sizeof(find_tests2)/sizeof(struct find_s));
198
199   DestroyWindow(hwndRichEdit);
200 }
201
202 static const struct getline_s {
203   int line;
204   size_t buffer_len;
205   const char *text;
206 } gl[] = {
207   {0, 10, "foo bar\r"},
208   {1, 10, "\r"},
209   {2, 10, "bar\r"},
210   {3, 10, "\r"},
211
212   /* Buffer smaller than line length */
213   {0, 2, "foo bar\r"},
214   {0, 1, "foo bar\r"},
215   {0, 0, "foo bar\r"}
216 };
217
218 static void test_EM_GETLINE(void)
219 {
220   int i;
221   HWND hwndRichEdit = new_richedit(NULL);
222   static const int nBuf = 1024;
223   char dest[1024], origdest[1024];
224   const char text[] = "foo bar\n"
225       "\n"
226       "bar\n";
227
228   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
229
230   memset(origdest, 0xBB, nBuf);
231   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
232   {
233     int nCopied;
234     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
235     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
236     memset(dest, 0xBB, nBuf);
237     *(WORD *) dest = gl[i].buffer_len;
238
239     /* EM_GETLINE appends a "\r\0" to the end of the line
240      * nCopied counts up to and including the '\r' */
241     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
242     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
243        expected_nCopied);
244     /* two special cases since a parameter is passed via dest */
245     if (gl[i].buffer_len == 0)
246       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
247          "buffer_len=0\n");
248     else if (gl[i].buffer_len == 1)
249       ok(dest[0] == gl[i].text[0] && !dest[1] &&
250          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
251     else
252     {
253       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
254          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
255       ok(!strncmp(dest + expected_bytes_written, origdest
256                   + expected_bytes_written, nBuf - expected_bytes_written),
257          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
258     }
259   }
260
261   DestroyWindow(hwndRichEdit);
262 }
263
264 static int get_scroll_pos_y(HWND hwnd)
265 {
266   POINT p = {-1, -1};
267   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
268   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
269   return p.y;
270 }
271
272 static void move_cursor(HWND hwnd, long charindex)
273 {
274   CHARRANGE cr;
275   cr.cpMax = charindex;
276   cr.cpMin = charindex;
277   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
278 }
279
280 static void line_scroll(HWND hwnd, int amount)
281 {
282   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
283 }
284
285 static void test_EM_SCROLLCARET(void)
286 {
287   int prevY, curY;
288   HWND hwndRichEdit = new_richedit(NULL);
289   const char text[] = "aa\n"
290       "this is a long line of text that should be longer than the "
291       "control's width\n"
292       "cc\n"
293       "dd\n"
294       "ee\n"
295       "ff\n"
296       "gg\n"
297       "hh\n";
298
299   /* Can't verify this */
300   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
301
302   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
303
304   /* Caret above visible window */
305   line_scroll(hwndRichEdit, 3);
306   prevY = get_scroll_pos_y(hwndRichEdit);
307   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
308   curY = get_scroll_pos_y(hwndRichEdit);
309   ok(prevY != curY, "%d == %d\n", prevY, curY);
310
311   /* Caret below visible window */
312   move_cursor(hwndRichEdit, sizeof(text) - 1);
313   line_scroll(hwndRichEdit, -3);
314   prevY = get_scroll_pos_y(hwndRichEdit);
315   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
316   curY = get_scroll_pos_y(hwndRichEdit);
317   ok(prevY != curY, "%d == %d\n", prevY, curY);
318
319   /* Caret in visible window */
320   move_cursor(hwndRichEdit, sizeof(text) - 2);
321   prevY = get_scroll_pos_y(hwndRichEdit);
322   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
323   curY = get_scroll_pos_y(hwndRichEdit);
324   ok(prevY == curY, "%d != %d\n", prevY, curY);
325
326   /* Caret still in visible window */
327   line_scroll(hwndRichEdit, -1);
328   prevY = get_scroll_pos_y(hwndRichEdit);
329   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
330   curY = get_scroll_pos_y(hwndRichEdit);
331   ok(prevY == curY, "%d != %d\n", prevY, curY);
332
333   DestroyWindow(hwndRichEdit);
334 }
335
336 static void test_EM_SETTEXTMODE(void)
337 {
338   HWND hwndRichEdit = new_richedit(NULL);
339   CHARFORMAT2 cf2, cf2test;
340   CHARRANGE cr;
341   int rc = 0;
342
343   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
344   /*Insert text into the control*/
345
346   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
347
348   /*Attempt to change the control to plain text mode*/
349   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
350   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
351
352   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
353   If rich text is pasted, it should have the same formatting as the rest
354   of the text in the control*/
355
356   /*Italicize the text
357   *NOTE: If the default text was already italicized, the test will simply
358   reverse; in other words, it will copy a regular "wine" into a plain
359   text window that uses an italicized format*/
360   cf2.cbSize = sizeof(CHARFORMAT2);
361   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
362              (LPARAM) &cf2);
363
364   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
365   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
366
367   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
368   however, SCF_ALL has been implemented*/
369   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
370   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
371
372   /*Select the string "wine"*/
373   cr.cpMin = 0;
374   cr.cpMax = 4;
375   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
376
377   /*Copy the italicized "wine" to the clipboard*/
378   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
379
380   /*Reset the formatting to default*/
381   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
382   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
383
384   /*Clear the text in the control*/
385   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
386
387   /*Switch to Plain Text Mode*/
388   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
389   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
390
391   /*Input "wine" again in normal format*/
392   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
393
394   /*Paste the italicized "wine" into the control*/
395   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
396
397   /*Select a character from the first "wine" string*/
398   cr.cpMin = 2;
399   cr.cpMax = 3;
400   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401
402   /*Retrieve its formatting*/
403   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
404               (LPARAM) &cf2);
405
406   /*Select a character from the second "wine" string*/
407   cr.cpMin = 5;
408   cr.cpMax = 6;
409   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
410
411   /*Retrieve its formatting*/
412   cf2test.cbSize = sizeof(CHARFORMAT2);
413   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
414                (LPARAM) &cf2test);
415
416   /*Compare the two formattings*/
417     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
418       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
419        cf2.dwEffects, cf2test.dwEffects);
420   /*Test TM_RICHTEXT by: switching back to Rich Text mode
421                          printing "wine" in the current format(normal)
422                          pasting "wine" from the clipboard(italicized)
423                          comparing the two formats(should differ)*/
424
425   /*Attempt to switch with text in control*/
426   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
427   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
428
429   /*Clear control*/
430   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
431
432   /*Switch into Rich Text mode*/
433   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
434   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
435
436   /*Print "wine" in normal formatting into the control*/
437   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
438
439   /*Paste italicized "wine" into the control*/
440   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
441
442   /*Select text from the first "wine" string*/
443   cr.cpMin = 1;
444   cr.cpMax = 3;
445   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446
447   /*Retrieve its formatting*/
448   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
449                 (LPARAM) &cf2);
450
451   /*Select text from the second "wine" string*/
452   cr.cpMin = 6;
453   cr.cpMax = 7;
454   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
455
456   /*Retrieve its formatting*/
457   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
458                 (LPARAM) &cf2test);
459
460   /*Test that the two formattings are not the same*/
461   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
462       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
463       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
464
465   DestroyWindow(hwndRichEdit);
466 }
467
468 static void test_TM_PLAINTEXT(void)
469 {
470   /*Tests plain text properties*/
471
472   HWND hwndRichEdit = new_richedit(NULL);
473   CHARFORMAT2 cf2, cf2test;
474   CHARRANGE cr;
475
476   /*Switch to plain text mode*/
477
478   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
479   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
480
481   /*Fill control with text*/
482
483   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
484
485   /*Select some text and bold it*/
486
487   cr.cpMin = 10;
488   cr.cpMax = 20;
489   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
490   cf2.cbSize = sizeof(CHARFORMAT2);
491   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
492               (LPARAM) &cf2);
493
494   cf2.dwMask = CFM_BOLD | cf2.dwMask;
495   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
496
497   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
498
499   /*Get the formatting of those characters*/
500
501   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
502
503   /*Get the formatting of some other characters*/
504   cf2test.cbSize = sizeof(CHARFORMAT2);
505   cr.cpMin = 21;
506   cr.cpMax = 30;
507   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
508   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
509
510   /*Test that they are the same as plain text allows only one formatting*/
511
512   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
513      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
514      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
515   
516   /*Fill the control with a "wine" string, which when inserted will be bold*/
517
518   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
519
520   /*Copy the bolded "wine" string*/
521
522   cr.cpMin = 0;
523   cr.cpMax = 4;
524   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
525   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
526
527   /*Swap back to rich text*/
528
529   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
530   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
531
532   /*Set the default formatting to bold italics*/
533
534   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
535   cf2.dwMask |= CFM_ITALIC;
536   cf2.dwEffects ^= CFE_ITALIC;
537   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
538
539   /*Set the text in the control to "wine", which will be bold and italicized*/
540
541   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
542
543   /*Paste the plain text "wine" string, which should take the insert
544    formatting, which at the moment is bold italics*/
545
546   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
547
548   /*Select the first "wine" string and retrieve its formatting*/
549
550   cr.cpMin = 1;
551   cr.cpMax = 3;
552   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
553   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
554
555   /*Select the second "wine" string and retrieve its formatting*/
556
557   cr.cpMin = 5;
558   cr.cpMax = 7;
559   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
560   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
561
562   /*Compare the two formattings. They should be the same.*/
563
564   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
565      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
566      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
567   DestroyWindow(hwndRichEdit);
568 }
569
570 static void test_WM_GETTEXT(void)
571 {
572     HWND hwndRichEdit = new_richedit(NULL);
573     static const char text[] = "Hello. My name is RichEdit!";
574     char buffer[1024] = {0};
575     int result;
576
577     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
578     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
579     result = strcmp(buffer,text);
580     ok(result == 0, 
581         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
582     DestroyWindow(hwndRichEdit);
583 }
584
585 /* FIXME: need to test unimplemented options and robustly test wparam */
586 static void test_EM_SETOPTIONS(void)
587 {
588     HWND hwndRichEdit = new_richedit(NULL);
589     static const char text[] = "Hello. My name is RichEdit!";
590     char buffer[1024] = {0};
591
592     /* NEGATIVE TESTING - NO OPTIONS SET */
593     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
594     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
595
596     /* testing no readonly by sending 'a' to the control*/
597     SetFocus(hwndRichEdit);
598     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
599     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600     ok(buffer[0]=='a', 
601        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
602     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
603
604     /* READONLY - sending 'a' to the control */
605     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
606     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
607     SetFocus(hwndRichEdit);
608     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
609     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
610     ok(buffer[0]==text[0], 
611        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
612
613     DestroyWindow(hwndRichEdit);
614 }
615
616 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
617 {
618   CHARFORMAT2W text_format;
619   int link_present = 0;
620   text_format.cbSize = sizeof(text_format);
621   SendMessage(hwnd, EM_SETSEL, 0, 0);
622   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
623   link_present = text_format.dwEffects & CFE_LINK;
624   if (is_url) 
625   { /* control text is url; should get CFE_LINK */
626         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
627   }
628   else 
629   {
630     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
631   }
632 }
633
634 static HWND new_static_wnd(HWND parent) {
635   return new_window("Static", 0, parent);
636 }
637
638 static void test_EM_AUTOURLDETECT(void)
639 {
640   struct urls_s {
641     const char *text;
642     int is_url;
643   } urls[12] = {
644     {"winehq.org", 0},
645     {"http://www.winehq.org", 1},
646     {"http//winehq.org", 0},
647     {"ww.winehq.org", 0},
648     {"www.winehq.org", 1},
649     {"ftp://192.168.1.1", 1},
650     {"ftp//192.168.1.1", 0},
651     {"mailto:your@email.com", 1},    
652     {"prospero:prosperoserver", 1},
653     {"telnet:test", 1},
654     {"news:newserver", 1},
655     {"wais:waisserver", 1}  
656   };
657
658   int i;
659   int urlRet=-1;
660   HWND hwndRichEdit, parent;
661
662   parent = new_static_wnd(NULL);
663   hwndRichEdit = new_richedit(parent);
664   /* Try and pass EM_AUTOURLDETECT some test wParam values */
665   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
666   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
667   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
668   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
669   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
670   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
671   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
672   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
673   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
674   /* for each url, check the text to see if CFE_LINK effect is present */
675   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
676     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
677     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
678     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
679     check_CFE_LINK_rcvd(hwndRichEdit, 0);
680     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
681     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
682     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
683     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
684   }
685   DestroyWindow(hwndRichEdit);
686   DestroyWindow(parent);
687 }
688
689 static void test_EM_SCROLL(void)
690 {
691   int i, j;
692   int r; /* return value */
693   int expr; /* expected return value */
694   HWND hwndRichEdit = new_richedit(NULL);
695   int y_before, y_after; /* units of lines of text */
696
697   /* test a richedit box containing a single line of text */
698   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
699   expr = 0x00010000;
700   for (i = 0; i < 4; i++) {
701     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
702
703     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
704     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
705     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
706        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
707     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
708        "(i == %d)\n", y_after, i);
709   }
710
711   /*
712    * test a richedit box that will scroll. There are two general
713    * cases: the case without any long lines and the case with a long
714    * line.
715    */
716   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
717     if (i == 0)
718       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
719     else
720       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
721                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
723                   "LONG LINE \nb\nc\nd\ne");
724     for (j = 0; j < 12; j++) /* reset scrol position to top */
725       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
726
727     /* get first visible line */
728     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
729     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
730
731     /* get new current first visible line */
732     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
733
734     ok(((r & 0xffffff00) == 0x00010000) &&
735        ((r & 0x000000ff) != 0x00000000),
736        "EM_SCROLL page down didn't scroll by a small positive number of "
737        "lines (r == 0x%08x)\n", r);
738     ok(y_after > y_before, "EM_SCROLL page down not functioning "
739        "(line %d scrolled to line %d\n", y_before, y_after);
740
741     y_before = y_after;
742     
743     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
744     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
745     ok(((r & 0xffffff00) == 0x0001ff00),
746        "EM_SCROLL page up didn't scroll by a small negative number of lines "
747        "(r == 0x%08x)\n", r);
748     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
749        "%d scrolled to line %d\n", y_before, y_after);
750     
751     y_before = y_after;
752
753     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
754
755     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
756
757     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
758        "(r == 0x%08x)\n", r);
759     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
760        "1 line (%d scrolled to %d)\n", y_before, y_after);
761
762     y_before = y_after;
763
764     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
765
766     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
767
768     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
769        "(r == 0x%08x)\n", r);
770     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
771        "line (%d scrolled to %d)\n", y_before, y_after);
772
773     y_before = y_after;
774
775     r = SendMessage(hwndRichEdit, EM_SCROLL,
776                     SB_LINEUP, 0); /* lineup beyond top */
777
778     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
779
780     ok(r == 0x00010000,
781        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
782     ok(y_before == y_after,
783        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
784
785     y_before = y_after;
786
787     r = SendMessage(hwndRichEdit, EM_SCROLL,
788                     SB_PAGEUP, 0);/*page up beyond top */
789
790     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
791
792     ok(r == 0x00010000,
793        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
794     ok(y_before == y_after,
795        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
796
797     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
798       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
799     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
800     r = SendMessage(hwndRichEdit, EM_SCROLL,
801                     SB_PAGEDOWN, 0); /* page down beyond bot */
802     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
803
804     ok(r == 0x00010000,
805        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
806     ok(y_before == y_after,
807        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
808        y_before, y_after);
809
810     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
811     SendMessage(hwndRichEdit, EM_SCROLL,
812                 SB_LINEDOWN, 0); /* line down beyond bot */
813     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
814     
815     ok(r == 0x00010000,
816        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
817     ok(y_before == y_after,
818        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
819        y_before, y_after);
820   }
821   DestroyWindow(hwndRichEdit);
822 }
823
824 static void test_EM_SETUNDOLIMIT(void)
825 {
826   /* cases we test for:
827    * default behaviour - limiting at 100 undo's 
828    * undo disabled - setting a limit of 0
829    * undo limited -  undo limit set to some to some number, like 2
830    * bad input - sending a negative number should default to 100 undo's */
831  
832   HWND hwndRichEdit = new_richedit(NULL);
833   CHARRANGE cr;
834   int i;
835   int result;
836   
837   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
838   cr.cpMin = 0;
839   cr.cpMax = 1;
840   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
841     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
842       also, multiple pastes don't combine like WM_CHAR would */
843   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
844
845   /* first case - check the default */
846   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
847   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
848     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
849   for (i=0; i<100; i++) /* Undo 100 of them */
850     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
851   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
852      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
853
854   /* second case - cannot undo */
855   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
856   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
857   SendMessage(hwndRichEdit,
858               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
859   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
860      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
861
862   /* third case - set it to an arbitrary number */
863   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
864   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
865   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
867   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
868   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
869   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
870      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
871   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
872   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
873      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
874   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
875   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
876      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
877   
878   /* fourth case - setting negative numbers should default to 100 undos */
879   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
880   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
881   ok (result == 100, 
882       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
883       
884   DestroyWindow(hwndRichEdit);
885 }
886
887 static void test_ES_PASSWORD(void)
888 {
889   /* This isn't hugely testable, so we're just going to run it through its paces */
890
891   HWND hwndRichEdit = new_richedit(NULL);
892   WCHAR result;
893
894   /* First, check the default of a regular control */
895   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
896   ok (result == 0,
897         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
898
899   /* Now, set it to something normal */
900   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
901   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
902   ok (result == 120,
903         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
904
905   /* Now, set it to something odd */
906   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
907   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
908   ok (result == 1234,
909         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
910   DestroyWindow(hwndRichEdit);
911 }
912
913 static void test_EM_SETTEXTEX(void)
914 {
915   HWND hwndRichEdit = new_richedit(NULL);
916   SETTEXTEX setText;
917   GETTEXTEX getText;
918   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
919                        'S', 'o', 'm', 'e', 
920                        'T', 'e', 'x', 't', 0}; 
921 #define MAX_BUF_LEN 1024
922   WCHAR buf[MAX_BUF_LEN];
923   int result;
924   CHARRANGE cr;
925
926   setText.codepage = 1200;  /* no constant for unicode */
927   getText.codepage = 1200;  /* no constant for unicode */
928   getText.cb = MAX_BUF_LEN;
929   getText.flags = GT_DEFAULT;
930
931   setText.flags = 0;
932   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
933   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
934   ok(lstrcmpW(buf, TestItem1) == 0,
935       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
936
937   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
938                        (WPARAM)&setText, (LPARAM) NULL);
939   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
940   
941   ok (result == 1, 
942       "EM_SETTEXTEX returned %d, instead of 1\n",result);
943   ok(lstrlenW(buf) == 0,
944       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
945   
946   /* put some text back */
947   setText.flags = 0;
948   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
949   /* select some text */
950   cr.cpMax = 1;
951   cr.cpMin = 3;
952   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
953   /* replace current selection */
954   setText.flags = ST_SELECTION;
955   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
956                        (WPARAM)&setText, (LPARAM) NULL);
957   ok(result == 0,
958       "EM_SETTEXTEX with NULL lParam to replace selection"
959       " with no text should return 0. Got %i\n",
960       result);
961   
962   /* put some text back */
963   setText.flags = 0;
964   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
965   /* select some text */
966   cr.cpMax = 1;
967   cr.cpMin = 3;
968   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
969   /* replace current selection */
970   setText.flags = ST_SELECTION;
971   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
972                        (WPARAM)&setText, (LPARAM) TestItem1);
973   /* get text */
974   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
975   ok(result == lstrlenW(TestItem1),
976       "EM_SETTEXTEX with NULL lParam to replace selection"
977       " with no text should return 0. Got %i\n",
978       result);
979   ok(lstrlenW(buf) == 22,
980       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
981       lstrlenW(buf) );
982
983   DestroyWindow(hwndRichEdit);
984 }
985
986 static void test_EM_LIMITTEXT(void)
987 {
988   int ret;
989
990   HWND hwndRichEdit = new_richedit(NULL);
991
992   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
993    * about setting the length to -1 for multiline edit controls doesn't happen.
994    */
995
996   /* Don't check default gettextlimit case. That's done in other tests */
997
998   /* Set textlimit to 100 */
999   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1000   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1001   ok (ret == 100,
1002       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1003
1004   /* Set textlimit to 0 */
1005   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1006   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1007   ok (ret == 65536,
1008       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1009
1010   /* Set textlimit to -1 */
1011   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1012   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1013   ok (ret == -1,
1014       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1015
1016   /* Set textlimit to -2 */
1017   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1018   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1019   ok (ret == -2,
1020       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1021
1022   DestroyWindow (hwndRichEdit);
1023 }
1024
1025
1026 static void test_EM_EXLIMITTEXT(void)
1027 {
1028   int i, selBegin, selEnd, len1, len2;
1029   int result;
1030   char text[1024 + 1];
1031   char buffer[1024 + 1];
1032   int textlimit = 0; /* multiple of 100 */
1033   HWND hwndRichEdit = new_richedit(NULL);
1034   
1035   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1036   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1037   
1038   textlimit = 256000;
1039   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1040   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1041   /* set higher */
1042   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1043   
1044   textlimit = 1000;
1045   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1046   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1047   /* set lower */
1048   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1049  
1050   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1051   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1052   /* default for WParam = 0 */
1053   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1054  
1055   textlimit = sizeof(text)-1;
1056   memset(text, 'W', textlimit);
1057   text[sizeof(text)-1] = 0;
1058   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1059   /* maxed out text */
1060   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1061   
1062   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1063   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1064   len1 = selEnd - selBegin;
1065   
1066   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1067   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1068   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1069   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1070   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1071   len2 = selEnd - selBegin;
1072   
1073   ok(len1 != len2,
1074     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1075     len1,len2,i);
1076   
1077   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1078   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1079   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1080   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1081   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1082   len1 = selEnd - selBegin;
1083   
1084   ok(len1 != len2,
1085     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1086     len1,len2,i);
1087   
1088   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1089   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1090   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1091   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1092   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1093   len2 = selEnd - selBegin;
1094   
1095   ok(len1 == len2, 
1096     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1097     len1,len2,i);
1098
1099   /* set text up to the limit, select all the text, then add a char */
1100   textlimit = 5;
1101   memset(text, 'W', textlimit);
1102   text[textlimit] = 0;
1103   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1104   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1105   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1106   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1107   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1108   result = strcmp(buffer, "A");
1109   ok(0 == result, "got string = \"%s\"\n", buffer);
1110
1111   DestroyWindow(hwndRichEdit);
1112 }
1113
1114 static void test_EM_GETLIMITTEXT(void)
1115 {
1116   int i;
1117   HWND hwndRichEdit = new_richedit(NULL);
1118
1119   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1120   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1121
1122   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1123   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1124   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1125
1126   DestroyWindow(hwndRichEdit);
1127 }
1128
1129 static void test_WM_SETFONT(void)
1130 {
1131   /* There is no invalid input or error conditions for this function.
1132    * NULL wParam and lParam just fall back to their default values 
1133    * It should be noted that even if you use a gibberish name for your fonts
1134    * here, it will still work because the name is stored. They will display as
1135    * System, but will report their name to be whatever they were created as */
1136   
1137   HWND hwndRichEdit = new_richedit(NULL);
1138   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1139     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1140     FF_DONTCARE, "Marlett");
1141   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1142     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1143     FF_DONTCARE, "MS Sans Serif");
1144   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1145     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1146     FF_DONTCARE, "Courier");
1147   LOGFONTA sentLogFont;
1148   CHARFORMAT2A returnedCF2A;
1149   
1150   returnedCF2A.cbSize = sizeof(returnedCF2A);
1151   
1152   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1153   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1154   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1155
1156   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1157   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1158     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1159     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1160
1161   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1162   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1163   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1164   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1165     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1166     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1167     
1168   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1169   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1170   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1171   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1172     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1173     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1174    
1175   /* This last test is special since we send in NULL. We clear the variables
1176    * and just compare to "System" instead of the sent in font name. */
1177   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1178   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1179   returnedCF2A.cbSize = sizeof(returnedCF2A);
1180   
1181   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1182   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1183   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1184   ok (!strcmp("System",returnedCF2A.szFaceName),
1185     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1186   
1187   DestroyWindow(hwndRichEdit);
1188 }
1189
1190
1191 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1192                                          LPBYTE pbBuff,
1193                                          LONG cb,
1194                                          LONG *pcb)
1195 {
1196   const char** str = (const char**)dwCookie;
1197   int size = strlen(*str);
1198   if(size > 3)  /* let's make it peice-meal for fun */
1199     size = 3;
1200   *pcb = cb;
1201   if (*pcb > size) {
1202     *pcb = size;
1203   }
1204   if (*pcb > 0) {
1205     memcpy(pbBuff, *str, *pcb);
1206     *str += *pcb;
1207   }
1208   return 0;
1209 }
1210
1211 static void test_EM_GETMODIFY(void)
1212 {
1213   HWND hwndRichEdit = new_richedit(NULL);
1214   LRESULT result;
1215   SETTEXTEX setText;
1216   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1217                        'S', 'o', 'm', 'e', 
1218                        'T', 'e', 'x', 't', 0}; 
1219   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1220                        'S', 'o', 'm', 'e', 
1221                        'O', 't', 'h', 'e', 'r',
1222                        'T', 'e', 'x', 't', 0}; 
1223   const char* streamText = "hello world";
1224   CHARFORMAT2 cf2;
1225   PARAFORMAT2 pf2;
1226   EDITSTREAM es;
1227   
1228   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1229     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1230     FF_DONTCARE, "Courier");
1231   
1232   setText.codepage = 1200;  /* no constant for unicode */
1233   setText.flags = ST_KEEPUNDO;
1234   
1235
1236   /* modify flag shouldn't be set when richedit is first created */
1237   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1238   ok (result == 0, 
1239       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1240   
1241   /* setting modify flag should actually set it */
1242   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1243   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1244   ok (result != 0, 
1245       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1246   
1247   /* clearing modify flag should actually clear it */
1248   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1249   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1250   ok (result == 0, 
1251       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1252  
1253   /* setting font doesn't change modify flag */
1254   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1255   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1256   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1257   ok (result == 0,
1258       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1259
1260   /* setting text should set modify flag */
1261   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1262   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1263   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1264   ok (result != 0,
1265       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1266   
1267   /* undo previous text doesn't reset modify flag */
1268   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1269   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1270   ok (result != 0,
1271       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1272   
1273   /* set text with no flag to keep undo stack should not set modify flag */
1274   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1275   setText.flags = 0;
1276   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1277   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1278   ok (result == 0,
1279       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1280   
1281   /* WM_SETTEXT doesn't modify */
1282   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1283   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1284   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1285   todo_wine {
1286   ok (result == 0,
1287       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1288   }
1289   
1290   /* clear the text */
1291   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1292   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1293   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1294   ok (result == 0,
1295       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1296   
1297   /* replace text */
1298   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1299   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1300   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1301   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1302   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1303   ok (result != 0,
1304       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1305   
1306   /* copy/paste text 1 */
1307   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1308   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1309   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1310   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1311   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1312   ok (result != 0,
1313       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1314   
1315   /* copy/paste text 2 */
1316   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1317   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1318   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1319   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1320   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1321   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1322   ok (result != 0,
1323       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1324   
1325   /* press char */
1326   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1327   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1328   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1329   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1330   ok (result != 0,
1331       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1332
1333   /* press del */
1334   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1335   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1336   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1337   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1338   ok (result != 0,
1339       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1340   
1341   /* set char format */
1342   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1343   cf2.cbSize = sizeof(CHARFORMAT2);
1344   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1345              (LPARAM) &cf2);
1346   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1347   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1348   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1349   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1350   ok (result != 0,
1351       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1352   
1353   /* set para format */
1354   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1355   pf2.cbSize = sizeof(PARAFORMAT2);
1356   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1357              (LPARAM) &pf2);
1358   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1359   pf2.wAlignment = PFA_RIGHT;
1360   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1361   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1362   ok (result == 0,
1363       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1364
1365   /* EM_STREAM */
1366   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1367   es.dwCookie = (DWORD_PTR)&streamText;
1368   es.dwError = 0;
1369   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1370   SendMessage(hwndRichEdit, EM_STREAMIN, 
1371               (WPARAM)(SF_TEXT), (LPARAM)&es);
1372   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1373   ok (result != 0,
1374       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1375
1376   DestroyWindow(hwndRichEdit);
1377 }
1378
1379 struct exsetsel_s {
1380   long min;
1381   long max;
1382   long expected_retval;
1383   int expected_getsel_start;
1384   int expected_getsel_end;
1385   int _exsetsel_todo_wine;
1386   int _getsel_todo_wine;
1387 };
1388
1389 const struct exsetsel_s exsetsel_tests[] = {
1390   /* sanity tests */
1391   {5, 10, 10, 5, 10, 0, 0},
1392   {15, 17, 17, 15, 17, 0, 0},
1393   /* test cpMax > strlen() */
1394   {0, 100, 18, 0, 18, 0, 1},
1395   /* test cpMin == cpMax */
1396   {5, 5, 5, 5, 5, 0, 0},
1397   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1398   {-1, 0, 5, 5, 5, 0, 0},
1399   {-1, 17, 5, 5, 5, 0, 0},
1400   {-1, 18, 5, 5, 5, 0, 0},
1401   /* test cpMin < 0 && cpMax < 0 */
1402   {-1, -1, 17, 17, 17, 0, 0},
1403   {-4, -5, 17, 17, 17, 0, 0},
1404   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1405   {0, -1, 18, 0, 18, 0, 1},
1406   {17, -5, 18, 17, 18, 0, 1},
1407   {18, -3, 17, 17, 17, 0, 0},
1408   /* test if cpMin > cpMax */
1409   {15, 19, 18, 15, 18, 0, 1},
1410   {19, 15, 18, 15, 18, 0, 1}
1411 };
1412
1413 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1414     CHARRANGE cr;
1415     long result;
1416     int start, end;
1417
1418     cr.cpMin = setsel->min;
1419     cr.cpMax = setsel->max;
1420     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1421
1422     if (setsel->_exsetsel_todo_wine) {
1423         todo_wine {
1424             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1425         }
1426     } else {
1427         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1428     }
1429
1430     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1431
1432     if (setsel->_getsel_todo_wine) {
1433         todo_wine {
1434             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);
1435         }
1436     } else {
1437         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);
1438     }
1439 }
1440
1441 static void test_EM_EXSETSEL(void)
1442 {
1443     HWND hwndRichEdit = new_richedit(NULL);
1444     int i;
1445     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1446
1447     /* sending some text to the window */
1448     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1449     /*                                                 01234567890123456*/
1450     /*                                                          10      */
1451
1452     for (i = 0; i < num_tests; i++) {
1453         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1454     }
1455
1456     DestroyWindow(hwndRichEdit);
1457 }
1458
1459 static void test_WM_PASTE(void)
1460 {
1461     int result;
1462     char buffer[1024] = {0};
1463     const char* text1 = "testing paste\r";
1464     const char* text2 = "testing paste\r\rtesting paste";
1465     const char* text3 = "testing paste\rpaste\rtesting paste";
1466     HWND hwndRichEdit = new_richedit(NULL);
1467
1468     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1469     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1470     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1471     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1472     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1473     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1474     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1475     result = strcmp(text1, buffer);
1476     ok(result == 0,
1477         "test paste: strcmp = %i\n", result);
1478
1479     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1480     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1481     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1482     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1483     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1484     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1485     SendMessage(hwndRichEdit, WM_CHAR, 25, 0);  /* ctrl-y */
1486     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1487     result = strcmp(buffer,text3);
1488     ok(result == 0,
1489         "test paste: strcmp = %i\n", result);
1490
1491     DestroyWindow(hwndRichEdit);
1492 }
1493
1494 static void test_EM_FORMATRANGE(void)
1495 {
1496   int r;
1497   FORMATRANGE fr;
1498   HDC hdc;
1499   HWND hwndRichEdit = new_richedit(NULL);
1500
1501   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1502
1503   hdc = GetDC(hwndRichEdit);
1504   ok(hdc != NULL, "Could not get HDC\n");
1505
1506   fr.hdc = fr.hdcTarget = hdc;
1507   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1508   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1509   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1510   fr.chrg.cpMin = 0;
1511   fr.chrg.cpMax = 20;
1512
1513   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1514   todo_wine {
1515     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1516   }
1517
1518   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1519   todo_wine {
1520     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1521   }
1522
1523   fr.chrg.cpMin = 0;
1524   fr.chrg.cpMax = 10;
1525
1526   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1527   todo_wine {
1528     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1529   }
1530
1531   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1532   todo_wine {
1533     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1534   }
1535
1536   DestroyWindow(hwndRichEdit);
1537 }
1538
1539 static int nCallbackCount = 0;
1540
1541 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1542                                  LONG cb, LONG* pcb)
1543 {
1544   const char text[] = {'t','e','s','t'};
1545
1546   if (sizeof(text) <= cb)
1547   {
1548     if ((int)dwCookie != nCallbackCount)
1549     {
1550       *pcb = 0;
1551       return 0;
1552     }
1553
1554     memcpy (pbBuff, text, sizeof(text));
1555     *pcb = sizeof(text);
1556
1557     nCallbackCount++;
1558
1559     return 0;
1560   }
1561   else
1562     return 1; /* indicates callback failed */
1563 }
1564
1565 static void test_EM_StreamIn_Undo(void)
1566 {
1567   /* The purpose of this test is to determine when a EM_StreamIn should be
1568    * undoable. This is important because WM_PASTE currently uses StreamIn and
1569    * pasting should always be undoable but streaming isn't always.
1570    *
1571    * cases to test:
1572    * StreamIn plain text without SFF_SELECTION.
1573    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1574    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1575    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1576    * Feel free to add tests for other text modes or StreamIn things.
1577    */
1578
1579
1580   HWND hwndRichEdit = new_richedit(NULL);
1581   LRESULT result;
1582   EDITSTREAM es;
1583   char buffer[1024] = {0};
1584   const char randomtext[] = "Some text";
1585
1586   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1587
1588   /* StreamIn, no SFF_SELECTION */
1589   es.dwCookie = nCallbackCount;
1590   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1591   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1592   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1593   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1594   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1595   result = strcmp (buffer,"test");
1596   ok (result  == 0,
1597       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1598
1599   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1600   ok (result == FALSE,
1601       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1602
1603   /* StreamIn, SFF_SELECTION, but nothing selected */
1604   es.dwCookie = nCallbackCount;
1605   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1606   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1607   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1608   SendMessage(hwndRichEdit, EM_STREAMIN,
1609               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1610   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1611   result = strcmp (buffer,"testSome text");
1612   ok (result  == 0,
1613       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1614
1615   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1616   ok (result == TRUE,
1617      "EM_STREAMIN with SFF_SELECTION but no selection set "
1618       "should create an undo\n");
1619
1620   /* StreamIn, SFF_SELECTION, with a selection */
1621   es.dwCookie = nCallbackCount;
1622   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1623   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1624   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1625   SendMessage(hwndRichEdit, EM_STREAMIN,
1626               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1627   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1628   result = strcmp (buffer,"Sometesttext");
1629   ok (result  == 0,
1630       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1631
1632   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1633   ok (result == TRUE,
1634       "EM_STREAMIN with SFF_SELECTION and selection set "
1635       "should create an undo\n");
1636
1637 }
1638
1639 static BOOL is_em_settextex_supported(HWND hwnd)
1640 {
1641     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1642     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1643 }
1644
1645 static void test_unicode_conversions(void)
1646 {
1647     static const WCHAR tW[] = {'t',0};
1648     static const WCHAR teW[] = {'t','e',0};
1649     static const WCHAR textW[] = {'t','e','s','t',0};
1650     static const char textA[] = "test";
1651     char bufA[64];
1652     WCHAR bufW[64];
1653     HWND hwnd;
1654     int is_win9x, em_settextex_supported, ret;
1655
1656     is_win9x = GetVersion() & 0x80000000;
1657
1658 #define set_textA(hwnd, wm_set_text, txt) \
1659     do { \
1660         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1661         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1662         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1663         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1664         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1665     } while(0)
1666 #define expect_textA(hwnd, wm_get_text, txt) \
1667     do { \
1668         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1669         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1670         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1671         memset(bufA, 0xAA, sizeof(bufA)); \
1672         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1673         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1674         ret = lstrcmpA(bufA, txt); \
1675         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1676     } while(0)
1677
1678 #define set_textW(hwnd, wm_set_text, txt) \
1679     do { \
1680         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1681         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1682         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1683         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1684         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1685     } while(0)
1686 #define expect_textW(hwnd, wm_get_text, txt) \
1687     do { \
1688         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1689         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1690         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1691         memset(bufW, 0xAA, sizeof(bufW)); \
1692         if (is_win9x) \
1693         { \
1694             assert(wm_get_text == EM_GETTEXTEX); \
1695             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1696             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1697         } \
1698         else \
1699         { \
1700             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1701             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1702         } \
1703         ret = lstrcmpW(bufW, txt); \
1704         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1705     } while(0)
1706 #define expect_empty(hwnd, wm_get_text) \
1707     do { \
1708         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1709         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1710         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1711         memset(bufA, 0xAA, sizeof(bufA)); \
1712         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1713         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1714         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1715     } while(0)
1716
1717     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1718                            0, 0, 200, 60, 0, 0, 0, 0);
1719     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1720
1721     ret = IsWindowUnicode(hwnd);
1722     if (is_win9x)
1723         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1724     else
1725         ok(ret, "RichEdit20W should be unicode under NT\n");
1726
1727     /* EM_SETTEXTEX is supported starting from version 3.0 */
1728     em_settextex_supported = is_em_settextex_supported(hwnd);
1729     trace("EM_SETTEXTEX is %ssupported on this platform\n",
1730           em_settextex_supported ? "" : "NOT ");
1731
1732     expect_empty(hwnd, WM_GETTEXT);
1733     expect_empty(hwnd, EM_GETTEXTEX);
1734
1735     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1736     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1737     expect_textA(hwnd, WM_GETTEXT, "t");
1738     expect_textA(hwnd, EM_GETTEXTEX, "t");
1739     expect_textW(hwnd, EM_GETTEXTEX, tW);
1740
1741     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1742     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1743     expect_textA(hwnd, WM_GETTEXT, "te");
1744     expect_textA(hwnd, EM_GETTEXTEX, "te");
1745     expect_textW(hwnd, EM_GETTEXTEX, teW);
1746
1747     set_textA(hwnd, WM_SETTEXT, NULL);
1748     expect_empty(hwnd, WM_GETTEXT);
1749     expect_empty(hwnd, EM_GETTEXTEX);
1750
1751     if (is_win9x)
1752         set_textA(hwnd, WM_SETTEXT, textW);
1753     else
1754         set_textA(hwnd, WM_SETTEXT, textA);
1755     expect_textA(hwnd, WM_GETTEXT, textA);
1756     expect_textA(hwnd, EM_GETTEXTEX, textA);
1757     expect_textW(hwnd, EM_GETTEXTEX, textW);
1758
1759     if (em_settextex_supported)
1760     {
1761         set_textA(hwnd, EM_SETTEXTEX, textA);
1762         expect_textA(hwnd, WM_GETTEXT, textA);
1763         expect_textA(hwnd, EM_GETTEXTEX, textA);
1764         expect_textW(hwnd, EM_GETTEXTEX, textW);
1765     }
1766
1767     if (!is_win9x)
1768     {
1769         set_textW(hwnd, WM_SETTEXT, textW);
1770         expect_textW(hwnd, WM_GETTEXT, textW);
1771         expect_textA(hwnd, WM_GETTEXT, textA);
1772         expect_textW(hwnd, EM_GETTEXTEX, textW);
1773         expect_textA(hwnd, EM_GETTEXTEX, textA);
1774
1775         if (em_settextex_supported)
1776         {
1777             set_textW(hwnd, EM_SETTEXTEX, textW);
1778             expect_textW(hwnd, WM_GETTEXT, textW);
1779             expect_textA(hwnd, WM_GETTEXT, textA);
1780             expect_textW(hwnd, EM_GETTEXTEX, textW);
1781             expect_textA(hwnd, EM_GETTEXTEX, textA);
1782         }
1783     }
1784     DestroyWindow(hwnd);
1785
1786     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1787                            0, 0, 200, 60, 0, 0, 0, 0);
1788     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1789
1790     ret = IsWindowUnicode(hwnd);
1791     ok(!ret, "RichEdit20A should NOT be unicode\n");
1792
1793     set_textA(hwnd, WM_SETTEXT, textA);
1794     expect_textA(hwnd, WM_GETTEXT, textA);
1795     expect_textA(hwnd, EM_GETTEXTEX, textA);
1796     expect_textW(hwnd, EM_GETTEXTEX, textW);
1797
1798     if (em_settextex_supported)
1799     {
1800         set_textA(hwnd, EM_SETTEXTEX, textA);
1801         expect_textA(hwnd, WM_GETTEXT, textA);
1802         expect_textA(hwnd, EM_GETTEXTEX, textA);
1803         expect_textW(hwnd, EM_GETTEXTEX, textW);
1804     }
1805
1806     if (!is_win9x)
1807     {
1808         set_textW(hwnd, WM_SETTEXT, textW);
1809         expect_textW(hwnd, WM_GETTEXT, textW);
1810         expect_textA(hwnd, WM_GETTEXT, textA);
1811         expect_textW(hwnd, EM_GETTEXTEX, textW);
1812         expect_textA(hwnd, EM_GETTEXTEX, textA);
1813
1814         if (em_settextex_supported)
1815         {
1816             set_textW(hwnd, EM_SETTEXTEX, textW);
1817             expect_textW(hwnd, WM_GETTEXT, textW);
1818             expect_textA(hwnd, WM_GETTEXT, textA);
1819             expect_textW(hwnd, EM_GETTEXTEX, textW);
1820             expect_textA(hwnd, EM_GETTEXTEX, textA);
1821         }
1822     }
1823     DestroyWindow(hwnd);
1824 }
1825
1826
1827 static void test_EM_GETTEXTLENGTHEX(void)
1828 {
1829     HWND hwnd;
1830     GETTEXTLENGTHEX gtl;
1831     int ret;
1832
1833     /* single line */
1834     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1835                            0, 0, 200, 60, 0, 0, 0, 0);
1836     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1837
1838     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1839     gtl.codepage = CP_ACP;
1840     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1841     ok(ret == 0, "ret %d\n",ret);
1842
1843     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1844     gtl.codepage = CP_ACP;
1845     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1846     ok(ret == 0, "ret %d\n",ret);
1847
1848     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
1849
1850     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1851     gtl.codepage = CP_ACP;
1852     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1853     todo_wine ok(ret == 1, "ret %d\n",ret);
1854
1855     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1856     gtl.codepage = CP_ACP;
1857     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1858     todo_wine ok(ret == 1, "ret %d\n",ret);
1859
1860     DestroyWindow(hwnd);
1861
1862     /* multi line */
1863     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
1864                            0, 0, 200, 60, 0, 0, 0, 0);
1865     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1866
1867     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1868     gtl.codepage = CP_ACP;
1869     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1870     todo_wine ok(ret == 0, "ret %d\n",ret);
1871
1872     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1873     gtl.codepage = CP_ACP;
1874     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1875     ok(ret == 0, "ret %d\n",ret);
1876
1877     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
1878
1879     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1880     gtl.codepage = CP_ACP;
1881     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1882     todo_wine ok(ret == 10, "ret %d\n",ret);
1883
1884     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1885     gtl.codepage = CP_ACP;
1886     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1887     ok(ret == 6, "ret %d\n",ret);
1888
1889     DestroyWindow(hwnd);
1890 }
1891
1892 START_TEST( editor )
1893 {
1894   MSG msg;
1895   time_t end;
1896
1897   /* Must explicitly LoadLibrary(). The test has no references to functions in
1898    * RICHED20.DLL, so the linker doesn't actually link to it. */
1899   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1900   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1901
1902   test_EM_FINDTEXT();
1903   test_EM_GETLINE();
1904   test_EM_SCROLLCARET();
1905   test_EM_SCROLL();
1906   test_EM_SETTEXTMODE();
1907   test_TM_PLAINTEXT();
1908   test_EM_SETOPTIONS();
1909   test_WM_GETTEXT();
1910   test_EM_AUTOURLDETECT();
1911   test_EM_SETUNDOLIMIT();
1912   test_ES_PASSWORD();
1913   test_EM_SETTEXTEX();
1914   test_EM_LIMITTEXT();
1915   test_EM_EXLIMITTEXT();
1916   test_EM_GETLIMITTEXT();
1917   test_WM_SETFONT();
1918   test_EM_GETMODIFY();
1919   test_EM_EXSETSEL();
1920   test_WM_PASTE();
1921   test_EM_StreamIn_Undo();
1922   test_EM_FORMATRANGE();
1923   test_unicode_conversions();
1924   test_EM_GETTEXTLENGTHEX();
1925
1926   /* Set the environment variable WINETEST_RICHED20 to keep windows
1927    * responsive and open for 30 seconds. This is useful for debugging.
1928    *
1929    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1930    * 50ms before retrying the queue. */
1931   end = time(NULL) + 30;
1932   if (getenv( "WINETEST_RICHED20" )) {
1933     while (time(NULL) < end) {
1934       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1935         TranslateMessage(&msg);
1936         DispatchMessage(&msg);
1937       } else {
1938         Sleep(50);
1939       }
1940     }
1941   }
1942
1943   OleFlushClipboard();
1944   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1945 }