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 #define COBJMACROS
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <wingdi.h>
31 #include <winuser.h>
32 #include <winnls.h>
33 #include <ole2.h>
34 #include <richedit.h>
35 #include <richole.h>
36 #include <commdlg.h>
37 #include <time.h>
38 #include <wine/test.h>
39 
40 #define ID_RICHEDITTESTDBUTTON 0x123
41 
42 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
43 
44 #define ok_w3(format, szString1, szString2, szString3) \
45     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
46     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
47     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
48     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
49        format, string1, string2, string3);
50 
51 static HMODULE hmoduleRichEdit;
52 static BOOL is_lang_japanese;
53 
54 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
55   HWND hwnd;
56   hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
57                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
58                       hmoduleRichEdit, NULL);
59   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60   return hwnd;
61 }
62 
63 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
64   HWND hwnd;
65   hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
66                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
67                       hmoduleRichEdit, NULL);
68   ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
69   return hwnd;
70 }
71 
72 static HWND new_richedit(HWND parent) {
73   return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
74 }
75 
76 static HWND new_richedit_with_style(HWND parent, DWORD style) {
77   return new_window(RICHEDIT_CLASS20A, style, parent);
78 }
79 
80 static HWND new_richeditW(HWND parent) {
81   return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
82 }
83 
84 /* Keeps the window reponsive for the deley_time in seconds.
85  * This is useful for debugging a test to see what is happening. */
86 static void keep_responsive(time_t delay_time)
87 {
88     MSG msg;
89     time_t end;
90 
91     /* The message pump uses PeekMessage() to empty the queue and then
92      * sleeps for 50ms before retrying the queue. */
93     end = time(NULL) + delay_time;
94     while (time(NULL) < end) {
95       if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
96         TranslateMessage(&msg);
97         DispatchMessageA(&msg);
98       } else {
99         Sleep(50);
100       }
101     }
102 }
103 
104 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 {
106     int ret;
107 
108     while (*szChars != '\0') {
109         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
110         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
111         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
112         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
113         szChars++;
114     }
115 }
116 
117 static BOOL hold_key(int vk)
118 {
119   BYTE key_state[256];
120   BOOL result;
121 
122   result = GetKeyboardState(key_state);
123   ok(result, "GetKeyboardState failed.\n");
124   if (!result) return FALSE;
125   key_state[vk] |= 0x80;
126   result = SetKeyboardState(key_state);
127   ok(result, "SetKeyboardState failed.\n");
128   return result != 0;
129 }
130 
131 static BOOL release_key(int vk)
132 {
133   BYTE key_state[256];
134   BOOL result;
135 
136   result = GetKeyboardState(key_state);
137   ok(result, "GetKeyboardState failed.\n");
138   if (!result) return FALSE;
139   key_state[vk] &= ~0x80;
140   result = SetKeyboardState(key_state);
141   ok(result, "SetKeyboardState failed.\n");
142   return result != 0;
143 }
144 
145 static const char haystack[] = "WINEWine wineWine wine WineWine";
146                              /* ^0        ^10       ^20       ^30 */
147 
148 struct find_s {
149   int start;
150   int end;
151   const char *needle;
152   int flags;
153   int expected_loc;
154 };
155 
156 
157 static struct find_s find_tests[] = {
158   /* Find in empty text */
159   {0, -1, "foo", FR_DOWN, -1},
160   {0, -1, "foo", 0, -1},
161   {0, -1, "", FR_DOWN, -1},
162   {20, 5, "foo", FR_DOWN, -1},
163   {5, 20, "foo", FR_DOWN, -1}
164 };
165 
166 static struct find_s find_tests2[] = {
167   /* No-result find */
168   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
169   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 
171   /* Subsequent finds */
172   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
173   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
174   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
175   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176 
177   /* Find backwards */
178   {19, 20, "Wine", FR_MATCHCASE, 13},
179   {10, 20, "Wine", FR_MATCHCASE, 4},
180   {20, 10, "Wine", FR_MATCHCASE, 13},
181 
182   /* Case-insensitive */
183   {1, 31, "wInE", FR_DOWN, 4},
184   {1, 31, "Wine", FR_DOWN, 4},
185 
186   /* High-to-low ranges */
187   {20, 5, "Wine", FR_DOWN, -1},
188   {2, 1, "Wine", FR_DOWN, -1},
189   {30, 29, "Wine", FR_DOWN, -1},
190   {20, 5, "Wine", 0, 13},
191 
192   /* Find nothing */
193   {5, 10, "", FR_DOWN, -1},
194   {10, 5, "", FR_DOWN, -1},
195   {0, -1, "", FR_DOWN, -1},
196   {10, 5, "", 0, -1},
197 
198   /* Whole-word search */
199   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
200   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
201   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
202   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
203   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
204   {11, -1, "winewine", FR_WHOLEWORD, 0},
205   {31, -1, "winewine", FR_WHOLEWORD, 23},
206 
207   /* Bad ranges */
208   {5, 200, "XXX", FR_DOWN, -1},
209   {-20, 20, "Wine", FR_DOWN, -1},
210   {-20, 20, "Wine", FR_DOWN, -1},
211   {-15, -20, "Wine", FR_DOWN, -1},
212   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 
214   /* Check the case noted in bug 4479 where matches at end aren't recognized */
215   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
216   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 
221   /* The backwards case of bug 4479; bounds look right
222    * Fails because backward find is wrong */
223   {19, 20, "WINE", FR_MATCHCASE, 0},
224   {0, 20, "WINE", FR_MATCHCASE, -1},
225 
226   {0, -1, "wineWine wine", 0, -1},
227 };
228 
229 static WCHAR *atowstr(const char *str)
230 {
231     WCHAR *ret;
232     DWORD len;
233     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
234     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
235     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
236     return ret;
237 }
238 
239 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
240 {
241   int findloc;
242 
243   if(unicode){
244       FINDTEXTW ftw;
245       memset(&ftw, 0, sizeof(ftw));
246       ftw.chrg.cpMin = f->start;
247       ftw.chrg.cpMax = f->end;
248       ftw.lpstrText = atowstr(f->needle);
249 
250       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
251       ok(findloc == f->expected_loc,
252          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
253          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254 
255       findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
256       ok(findloc == f->expected_loc,
257          "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
258          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
259 
260       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
261   }else{
262       FINDTEXTA fta;
263       memset(&fta, 0, sizeof(fta));
264       fta.chrg.cpMin = f->start;
265       fta.chrg.cpMax = f->end;
266       fta.lpstrText = f->needle;
267 
268       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
269       ok(findloc == f->expected_loc,
270          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
271          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
272   }
273 }
274 
275 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
276     int id, BOOL unicode)
277 {
278   int findloc;
279   int expected_end_loc;
280 
281   if(unicode){
282       FINDTEXTEXW ftw;
283       memset(&ftw, 0, sizeof(ftw));
284       ftw.chrg.cpMin = f->start;
285       ftw.chrg.cpMax = f->end;
286       ftw.lpstrText = atowstr(f->needle);
287       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
288       ok(findloc == f->expected_loc,
289           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
290           name, id, f->needle, f->start, f->end, f->flags, findloc);
291       ok(ftw.chrgText.cpMin == f->expected_loc,
292           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
293           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
294       expected_end_loc = ((f->expected_loc == -1) ? -1
295             : f->expected_loc + strlen(f->needle));
296       ok(ftw.chrgText.cpMax == expected_end_loc,
297           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
298           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
299       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
300   }else{
301       FINDTEXTEXA fta;
302       memset(&fta, 0, sizeof(fta));
303       fta.chrg.cpMin = f->start;
304       fta.chrg.cpMax = f->end;
305       fta.lpstrText = f->needle;
306       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
307       ok(findloc == f->expected_loc,
308           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
309           name, id, f->needle, f->start, f->end, f->flags, findloc);
310       ok(fta.chrgText.cpMin == f->expected_loc,
311           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
312           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
313       expected_end_loc = ((f->expected_loc == -1) ? -1
314             : f->expected_loc + strlen(f->needle));
315       ok(fta.chrgText.cpMax == expected_end_loc,
316           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
317           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
318   }
319 }
320 
321 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
322     int num_tests, BOOL unicode)
323 {
324   int i;
325 
326   for (i = 0; i < num_tests; i++) {
327       check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
328       check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
329   }
330 }
331 
332 static void test_EM_FINDTEXT(BOOL unicode)
333 {
334   HWND hwndRichEdit;
335   CHARFORMAT2A cf2;
336 
337   if(unicode)
338        hwndRichEdit = new_richeditW(NULL);
339   else
340        hwndRichEdit = new_richedit(NULL);
341 
342   /* Empty rich edit control */
343   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
344       sizeof(find_tests)/sizeof(struct find_s), unicode);
345 
346   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
347 
348   /* Haystack text */
349   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
350       sizeof(find_tests2)/sizeof(struct find_s), unicode);
351 
352   /* Setting a format on an arbitrary range should have no effect in search
353      results. This tests correct offset reporting across runs. */
354   cf2.cbSize = sizeof(CHARFORMAT2A);
355   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
356   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
357   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
358   SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
359   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
360 
361   /* Haystack text, again */
362   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
363       sizeof(find_tests2)/sizeof(struct find_s), unicode);
364 
365   /* Yet another range */
366   cf2.dwMask = CFM_BOLD | cf2.dwMask;
367   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
368   SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
369   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
370 
371   /* Haystack text, again */
372   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
373       sizeof(find_tests2)/sizeof(struct find_s), unicode);
374 
375   DestroyWindow(hwndRichEdit);
376 }
377 
378 static const struct getline_s {
379   int line;
380   size_t buffer_len;
381   const char *text;
382 } gl[] = {
383   {0, 10, "foo bar\r"},
384   {1, 10, "\r"},
385   {2, 10, "bar\r"},
386   {3, 10, "\r"},
387 
388   /* Buffer smaller than line length */
389   {0, 2, "foo bar\r"},
390   {0, 1, "foo bar\r"},
391   {0, 0, "foo bar\r"}
392 };
393 
394 static void test_EM_GETLINE(void)
395 {
396   int i;
397   HWND hwndRichEdit = new_richedit(NULL);
398   static const int nBuf = 1024;
399   char dest[1024], origdest[1024];
400   const char text[] = "foo bar\n"
401       "\n"
402       "bar\n";
403 
404   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
405 
406   memset(origdest, 0xBB, nBuf);
407   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
408   {
409     int nCopied;
410     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
411     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
412     memset(dest, 0xBB, nBuf);
413     *(WORD *) dest = gl[i].buffer_len;
414 
415     /* EM_GETLINE appends a "\r\0" to the end of the line
416      * nCopied counts up to and including the '\r' */
417     nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
418     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
419        expected_nCopied);
420     /* two special cases since a parameter is passed via dest */
421     if (gl[i].buffer_len == 0)
422       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
423          "buffer_len=0\n");
424     else if (gl[i].buffer_len == 1)
425       ok(dest[0] == gl[i].text[0] && !dest[1] &&
426          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
427     else
428     {
429       /* Prepare hex strings of buffers to dump on failure. */
430       char expectedbuf[1024];
431       char resultbuf[1024];
432       int j;
433       resultbuf[0] = '\0';
434       for (j = 0; j < 32; j++)
435         sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
436       expectedbuf[0] = '\0';
437       for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
438         sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
439       for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
440         sprintf(expectedbuf+strlen(expectedbuf), "??");
441       for (; j < 32; j++) /* Bytes after declared buffer size */
442         sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
443 
444       /* Test the part of the buffer that is expected to be written according
445        * to the MSDN documentation fo EM_GETLINE, which does not state that
446        * a NULL terminating character will be added unless no text is copied.
447        *
448        * Windows NT does not append a NULL terminating character, but
449        * Windows 2000 and up do append a NULL terminating character if there
450        * is space in the buffer. The test will ignore this difference. */
451       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
452          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
453          i, expected_bytes_written, expectedbuf, resultbuf);
454       /* Test the part of the buffer after the declared length to make sure
455        * there are no buffer overruns. */
456       ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
457                   nBuf - gl[i].buffer_len),
458          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
459          i, expected_bytes_written, expectedbuf, resultbuf);
460     }
461   }
462 
463   DestroyWindow(hwndRichEdit);
464 }
465 
466 static void test_EM_LINELENGTH(void)
467 {
468   HWND hwndRichEdit = new_richedit(NULL);
469   const char * text =
470         "richedit1\r"
471         "richedit1\n"
472         "richedit1\r\n"
473         "richedit1";
474   int offset_test[10][2] = {
475         {0, 9},
476         {5, 9},
477         {10, 9},
478         {15, 9},
479         {20, 9},
480         {25, 9},
481         {30, 9},
482         {35, 9},
483         {40, 0},
484         {45, 0},
485   };
486   int i;
487   LRESULT result;
488 
489   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
490 
491   for (i = 0; i < 10; i++) {
492     result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
493     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
494         offset_test[i][0], result, offset_test[i][1]);
495   }
496 
497   /* Test with multibyte character */
498   if (!is_lang_japanese)
499     skip("Skip multibyte character tests on non-Japanese platform\n");
500   else
501   {
502     const char *text1 =
503           "wine\n"
504           "richedit\x8e\xf0\n"
505           "wine";
506     int offset_test1[3][2] = {
507            {0, 4},  /* Line 1: |wine\n */
508            {5, 9},  /* Line 2: |richedit\x8e\xf0\n */
509            {15, 4}, /* Line 3: |wine */
510     };
511     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
512     for (i = 0; i < sizeof(offset_test1)/sizeof(offset_test1[0]); i++) {
513       result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
514       ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
515          offset_test1[i][0], result, offset_test1[i][1]);
516     }
517   }
518 
519   DestroyWindow(hwndRichEdit);
520 }
521 
522 static int get_scroll_pos_y(HWND hwnd)
523 {
524   POINT p = {-1, -1};
525   SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
526   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
527   return p.y;
528 }
529 
530 static void move_cursor(HWND hwnd, LONG charindex)
531 {
532   CHARRANGE cr;
533   cr.cpMax = charindex;
534   cr.cpMin = charindex;
535   SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
536 }
537 
538 static void line_scroll(HWND hwnd, int amount)
539 {
540   SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
541 }
542 
543 static void test_EM_SCROLLCARET(void)
544 {
545   int prevY, curY;
546   const char text[] = "aa\n"
547       "this is a long line of text that should be longer than the "
548       "control's width\n"
549       "cc\n"
550       "dd\n"
551       "ee\n"
552       "ff\n"
553       "gg\n"
554       "hh\n";
555   /* The richedit window height needs to be large enough vertically to fit in
556    * more than two lines of text, so the new_richedit function can't be used
557    * since a height of 60 was not large enough on some systems.
558    */
559   HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
560                                    ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
561                                    0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
562   ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
563 
564   /* Can't verify this */
565   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
566 
567   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
568 
569   /* Caret above visible window */
570   line_scroll(hwndRichEdit, 3);
571   prevY = get_scroll_pos_y(hwndRichEdit);
572   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
573   curY = get_scroll_pos_y(hwndRichEdit);
574   ok(prevY != curY, "%d == %d\n", prevY, curY);
575 
576   /* Caret below visible window */
577   move_cursor(hwndRichEdit, sizeof(text) - 1);
578   line_scroll(hwndRichEdit, -3);
579   prevY = get_scroll_pos_y(hwndRichEdit);
580   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
581   curY = get_scroll_pos_y(hwndRichEdit);
582   ok(prevY != curY, "%d == %d\n", prevY, curY);
583 
584   /* Caret in visible window */
585   move_cursor(hwndRichEdit, sizeof(text) - 2);
586   prevY = get_scroll_pos_y(hwndRichEdit);
587   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
588   curY = get_scroll_pos_y(hwndRichEdit);
589   ok(prevY == curY, "%d != %d\n", prevY, curY);
590 
591   /* Caret still in visible window */
592   line_scroll(hwndRichEdit, -1);
593   prevY = get_scroll_pos_y(hwndRichEdit);
594   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
595   curY = get_scroll_pos_y(hwndRichEdit);
596   ok(prevY == curY, "%d != %d\n", prevY, curY);
597 
598   DestroyWindow(hwndRichEdit);
599 }
600 
601 static void test_EM_POSFROMCHAR(void)
602 {
603   HWND hwndRichEdit = new_richedit(NULL);
604   int i, expected;
605   LRESULT result;
606   unsigned int height = 0;
607   int xpos = 0;
608   POINTL pt;
609   LOCALESIGNATURE sig;
610   BOOL rtl;
611   PARAFORMAT2 fmt;
612   static const char text[] = "aa\n"
613       "this is a long line of text that should be longer than the "
614       "control's width\n"
615       "cc\n"
616       "dd\n"
617       "ee\n"
618       "ff\n"
619       "gg\n"
620       "hh\n";
621 
622   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
623                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
624          (sig.lsUsb[3] & 0x08000000) != 0);
625 
626   /* Fill the control to lines to ensure that most of them are offscreen */
627   for (i = 0; i < 50; i++)
628   {
629     /* Do not modify the string; it is exactly 16 characters long. */
630     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
631     SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
632   }
633 
634   /*
635    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
636    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
637    Richedit 3.0 accepts either of the above API conventions.
638    */
639 
640   /* Testing Richedit 2.0 API format */
641 
642   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
643      Since all lines are identical and drawn with the same font,
644      they should have the same height... right?
645    */
646   for (i = 0; i < 50; i++)
647   {
648     /* All the lines are 16 characters long */
649     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
650     if (i == 0)
651     {
652       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
653       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
654       xpos = LOWORD(result);
655     }
656     else if (i == 1)
657     {
658       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
659       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
660       height = HIWORD(result);
661     }
662     else
663     {
664       ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
665       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
666     }
667   }
668 
669   /* Testing position at end of text */
670   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
671   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
672   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
673 
674   /* Testing position way past end of text */
675   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
676   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
677   expected = (rtl ? 8 : 1);
678   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
679 
680   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
681   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
682   for (i = 0; i < 50; i++)
683   {
684     /* All the lines are 16 characters long */
685     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
686     ok((signed short)(HIWORD(result)) == (i - 1) * height,
687         "EM_POSFROMCHAR reports y=%hd, expected %d\n",
688         (signed short)(HIWORD(result)), (i - 1) * height);
689     ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
690   }
691 
692   /* Testing position at end of text */
693   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
694   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
695   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
696 
697   /* Testing position way past end of text */
698   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
699   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
700   expected = (rtl ? 8 : 1);
701   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
702 
703   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
704   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
705   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
706 
707   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
708   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
709   ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
710   xpos = LOWORD(result);
711 
712   SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
713   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
714   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
715   ok((signed short)(LOWORD(result)) < xpos,
716         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
717         (signed short)(LOWORD(result)), xpos);
718   SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
719 
720   /* Test around end of text that doesn't end in a newline. */
721   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
722   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
723               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
724   ok(pt.x > 1, "pt.x = %d\n", pt.x);
725   xpos = pt.x;
726   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
727               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
728   ok(pt.x > xpos, "pt.x = %d\n", pt.x);
729   xpos = (rtl ? pt.x + 7 : pt.x);
730   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
731               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
732   ok(pt.x == xpos, "pt.x = %d\n", pt.x);
733 
734   /* Try a negative position. */
735   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
736   ok(pt.x == 1, "pt.x = %d\n", pt.x);
737 
738   /* test negative indentation */
739   SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
740           (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
741   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
742   ok(pt.x == 1, "pt.x = %d\n", pt.x);
743 
744   fmt.cbSize = sizeof(fmt);
745   SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
746   ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
747   ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
748   ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
749 
750   DestroyWindow(hwndRichEdit);
751 }
752 
753 static void test_EM_SETCHARFORMAT(void)
754 {
755   HWND hwndRichEdit = new_richedit(NULL);
756   CHARFORMAT2A cf2;
757   CHARFORMAT2W cfW;
758   int rc = 0;
759   int tested_effects[] = {
760     CFE_BOLD,
761     CFE_ITALIC,
762     CFE_UNDERLINE,
763     CFE_STRIKEOUT,
764     CFE_PROTECTED,
765     CFE_LINK,
766     CFE_SUBSCRIPT,
767     CFE_SUPERSCRIPT,
768     0
769   };
770   int i;
771   CHARRANGE cr;
772   LOCALESIGNATURE sig;
773   BOOL rtl;
774   DWORD expect_effects;
775 
776   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
777                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
778          (sig.lsUsb[3] & 0x08000000) != 0);
779 
780   /* check charformat defaults */
781   memset(&cf2, 0, sizeof(CHARFORMAT2A));
782   cf2.cbSize = sizeof(CHARFORMAT2A);
783   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
784   ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
785   expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
786   if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
787   ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
788   ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
789   ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
790   ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
791   ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
792   ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
793   ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
794   ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
795 
796   /* Invalid flags, CHARFORMAT2 structure blanked out */
797   memset(&cf2, 0, sizeof(cf2));
798   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
799   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
800 
801   /* A valid flag, CHARFORMAT2 structure blanked out */
802   memset(&cf2, 0, sizeof(cf2));
803   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
804   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
805 
806   /* A valid flag, CHARFORMAT2 structure blanked out */
807   memset(&cf2, 0, sizeof(cf2));
808   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
809   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
810 
811   /* A valid flag, CHARFORMAT2 structure blanked out */
812   memset(&cf2, 0, sizeof(cf2));
813   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
814   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
815 
816   /* A valid flag, CHARFORMAT2 structure blanked out */
817   memset(&cf2, 0, sizeof(cf2));
818   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
819   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
820 
821   /* Invalid flags, CHARFORMAT2 structure minimally filled */
822   memset(&cf2, 0, sizeof(cf2));
823   cf2.cbSize = sizeof(CHARFORMAT2A);
824   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
825   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
826   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
827   ok(rc == FALSE, "Should not be able to undo here.\n");
828   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
829 
830   /* A valid flag, CHARFORMAT2 structure minimally filled */
831   memset(&cf2, 0, sizeof(cf2));
832   cf2.cbSize = sizeof(CHARFORMAT2A);
833   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
834   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
835   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
836   ok(rc == FALSE, "Should not be able to undo here.\n");
837   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
838 
839   /* A valid flag, CHARFORMAT2 structure minimally filled */
840   memset(&cf2, 0, sizeof(cf2));
841   cf2.cbSize = sizeof(CHARFORMAT2A);
842   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
843   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
844   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
845   ok(rc == FALSE, "Should not be able to undo here.\n");
846   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
847 
848   /* A valid flag, CHARFORMAT2 structure minimally filled */
849   memset(&cf2, 0, sizeof(cf2));
850   cf2.cbSize = sizeof(CHARFORMAT2A);
851   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
852   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
853   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
854   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
855   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
856 
857   /* A valid flag, CHARFORMAT2 structure minimally filled */
858   memset(&cf2, 0, sizeof(cf2));
859   cf2.cbSize = sizeof(CHARFORMAT2A);
860   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
861   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
862   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
863   ok(rc == TRUE, "Should not be able to undo here.\n");
864   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
865 
866   cf2.cbSize = sizeof(CHARFORMAT2A);
867   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
868 
869   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
870   cf2.cbSize = sizeof(CHARFORMAT2A);
871   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
872   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
873   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
874 
875   /* wParam==0 is default char format, does not set modify */
876   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
877   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
878   ok(rc == 0, "Text marked as modified, expected not modified!\n");
879   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
880   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
881   if (! rtl)
882   {
883     rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
884     ok(rc == 0, "Text marked as modified, expected not modified!\n");
885   }
886   else
887     skip("RTL language found\n");
888 
889   /* wParam==SCF_SELECTION sets modify if nonempty selection */
890   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
891   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
892   ok(rc == 0, "Text marked as modified, expected not modified!\n");
893   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
894   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
895   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
896   ok(rc == 0, "Text marked as modified, expected not modified!\n");
897 
898   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
899   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
900   ok(rc == 0, "Text marked as modified, expected not modified!\n");
901   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
902   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
903   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
904   ok(rc == 0, "Text marked as modified, expected not modified!\n");
905   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
906   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
907   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
908   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
909   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
910 
911   /* wParam==SCF_ALL sets modify regardless of whether text is present */
912   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
913   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
914   ok(rc == 0, "Text marked as modified, expected not modified!\n");
915   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
916   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
917   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
918   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
919 
920   DestroyWindow(hwndRichEdit);
921 
922   /* EM_GETCHARFORMAT tests */
923   for (i = 0; tested_effects[i]; i++)
924   {
925     hwndRichEdit = new_richedit(NULL);
926     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
927 
928     /* Need to set a TrueType font to get consistent CFM_BOLD results */
929     memset(&cf2, 0, sizeof(CHARFORMAT2A));
930     cf2.cbSize = sizeof(CHARFORMAT2A);
931     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
932     cf2.dwEffects = 0;
933     strcpy(cf2.szFaceName, "Courier New");
934     cf2.wWeight = FW_DONTCARE;
935     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
936 
937     memset(&cf2, 0, sizeof(CHARFORMAT2A));
938     cf2.cbSize = sizeof(CHARFORMAT2A);
939     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
940     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
941     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
942           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
943           ||
944           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
945         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
946     ok((cf2.dwEffects & tested_effects[i]) == 0,
947         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
948 
949     memset(&cf2, 0, sizeof(CHARFORMAT2A));
950     cf2.cbSize = sizeof(CHARFORMAT2A);
951     cf2.dwMask = tested_effects[i];
952     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
953       cf2.dwMask = CFM_SUPERSCRIPT;
954     cf2.dwEffects = tested_effects[i];
955     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
956     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
957 
958     memset(&cf2, 0, sizeof(CHARFORMAT2A));
959     cf2.cbSize = sizeof(CHARFORMAT2A);
960     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
961     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
962     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
963           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
964           ||
965           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
966         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
967     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
968         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
969 
970     memset(&cf2, 0, sizeof(CHARFORMAT2A));
971     cf2.cbSize = sizeof(CHARFORMAT2A);
972     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
973     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
974     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
975           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
976           ||
977           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
978         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
979     ok((cf2.dwEffects & tested_effects[i]) == 0,
980         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
981 
982     memset(&cf2, 0, sizeof(CHARFORMAT2A));
983     cf2.cbSize = sizeof(CHARFORMAT2A);
984     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
985     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
986     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
987           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
988           ||
989           (cf2.dwMask & tested_effects[i]) == 0),
990         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
991 
992     DestroyWindow(hwndRichEdit);
993   }
994 
995   for (i = 0; tested_effects[i]; i++)
996   {
997     hwndRichEdit = new_richedit(NULL);
998     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
999 
1000     /* Need to set a TrueType font to get consistent CFM_BOLD results */
1001     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1002     cf2.cbSize = sizeof(CHARFORMAT2A);
1003     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1004     cf2.dwEffects = 0;
1005     strcpy(cf2.szFaceName, "Courier New");
1006     cf2.wWeight = FW_DONTCARE;
1007     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1008 
1009     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1010     cf2.cbSize = sizeof(CHARFORMAT2A);
1011     cf2.dwMask = tested_effects[i];
1012     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1013       cf2.dwMask = CFM_SUPERSCRIPT;
1014     cf2.dwEffects = tested_effects[i];
1015     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1016     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1017 
1018     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1019     cf2.cbSize = sizeof(CHARFORMAT2A);
1020     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1021     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1022     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1023           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1024           ||
1025           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1026         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1027     ok((cf2.dwEffects & tested_effects[i]) == 0,
1028         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1029 
1030     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1031     cf2.cbSize = sizeof(CHARFORMAT2A);
1032     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1033     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1034     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1035           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1036           ||
1037           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1038         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1039     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1040         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1041 
1042     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1043     cf2.cbSize = sizeof(CHARFORMAT2A);
1044     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1045     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1046     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1047           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1048           ||
1049           (cf2.dwMask & tested_effects[i]) == 0),
1050         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1051     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1052         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1053 
1054     DestroyWindow(hwndRichEdit);
1055   }
1056 
1057   /* Effects applied on an empty selection should take effect when selection is
1058      replaced with text */
1059   hwndRichEdit = new_richedit(NULL);
1060   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1061   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1062 
1063   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1064   cf2.cbSize = sizeof(CHARFORMAT2A);
1065   cf2.dwMask = CFM_BOLD;
1066   cf2.dwEffects = CFE_BOLD;
1067   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1068 
1069   /* Selection is now nonempty */
1070   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1071 
1072   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1073   cf2.cbSize = sizeof(CHARFORMAT2A);
1074   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1075   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1076 
1077   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1078       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1079   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1080       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1081 
1082 
1083   /* Set two effects on an empty selection */
1084   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1085   /* first clear bold, italic */
1086   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1087   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1088   cf2.cbSize = sizeof(CHARFORMAT2A);
1089   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1090   cf2.dwEffects = 0;
1091   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1092 
1093   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1094 
1095   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1096   cf2.cbSize = sizeof(CHARFORMAT2A);
1097   cf2.dwMask = CFM_BOLD;
1098   cf2.dwEffects = CFE_BOLD;
1099   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1100   cf2.dwMask = CFM_ITALIC;
1101   cf2.dwEffects = CFE_ITALIC;
1102   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1103 
1104   /* Selection is now nonempty */
1105   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1106 
1107   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1108   cf2.cbSize = sizeof(CHARFORMAT2A);
1109   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1110   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1111 
1112   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1113       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1114   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1115       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1116 
1117   /* Setting the (empty) selection to exactly the same place as before should
1118      NOT clear the insertion style! */
1119   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1120   /* first clear bold, italic */
1121   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1122   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1123   cf2.cbSize = sizeof(CHARFORMAT2A);
1124   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1125   cf2.dwEffects = 0;
1126   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1127 
1128   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1129 
1130   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1131   cf2.cbSize = sizeof(CHARFORMAT2A);
1132   cf2.dwMask = CFM_BOLD;
1133   cf2.dwEffects = CFE_BOLD;
1134   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1135 
1136   /* Empty selection in same place, insert style should NOT be forgotten here. */
1137   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1138 
1139   /* Selection is now nonempty */
1140   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1141 
1142   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1143   cf2.cbSize = sizeof(CHARFORMAT2A);
1144   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1145   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1146 
1147   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1148       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1149   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1150       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1151 
1152   /* Moving the selection will clear the insertion style */
1153   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1154   /* first clear bold, italic */
1155   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1156   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1157   cf2.cbSize = sizeof(CHARFORMAT2A);
1158   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1159   cf2.dwEffects = 0;
1160   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1161 
1162   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1163 
1164   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1165   cf2.cbSize = sizeof(CHARFORMAT2A);
1166   cf2.dwMask = CFM_BOLD;
1167   cf2.dwEffects = CFE_BOLD;
1168   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1169 
1170   /* Move selection and then put it back, insert style should be forgotten here. */
1171   SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1172   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1173 
1174   /* Selection is now nonempty */
1175   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1176 
1177   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1178   cf2.cbSize = sizeof(CHARFORMAT2A);
1179   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1180   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1181 
1182   ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1183       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1184   ok((cf2.dwEffects & CFE_BOLD) == 0,
1185       "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1186 
1187   /* Ditto with EM_EXSETSEL */
1188   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1189   /* first clear bold, italic */
1190   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1191   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1192   cf2.cbSize = sizeof(CHARFORMAT2A);
1193   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1194   cf2.dwEffects = 0;
1195   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1196 
1197   cr.cpMin = 2; cr.cpMax = 2;
1198   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1199 
1200   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1201   cf2.cbSize = sizeof(CHARFORMAT2A);
1202   cf2.dwMask = CFM_BOLD;
1203   cf2.dwEffects = CFE_BOLD;
1204   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1205 
1206   /* Empty selection in same place, insert style should NOT be forgotten here. */
1207   cr.cpMin = 2; cr.cpMax = 2;
1208   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1209 
1210   /* Selection is now nonempty */
1211   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1212 
1213   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1214   cf2.cbSize = sizeof(CHARFORMAT2A);
1215   cr.cpMin = 2; cr.cpMax = 6;
1216   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1217   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1218 
1219   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1220       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1221   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1222       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1223 
1224   /* show that wWeight is at the correct offset in CHARFORMAT2A */
1225   memset(&cf2, 0, sizeof(cf2));
1226   cf2.cbSize = sizeof(cf2);
1227   cf2.dwMask = CFM_WEIGHT;
1228   cf2.wWeight = 100;
1229   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1230   memset(&cf2, 0, sizeof(cf2));
1231   cf2.cbSize = sizeof(cf2);
1232   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1233   ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1234 
1235   memset(&cf2, 0, sizeof(cf2));
1236   cf2.cbSize = sizeof(cf2);
1237   cf2.dwMask = CFM_SPACING;
1238   cf2.sSpacing = 10;
1239   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1240   memset(&cf2, 0, sizeof(cf2));
1241   cf2.cbSize = sizeof(cf2);
1242   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1243   ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1244 
1245   /* show that wWeight is at the correct offset in CHARFORMAT2W */
1246   memset(&cfW, 0, sizeof(cfW));
1247   cfW.cbSize = sizeof(cfW);
1248   cfW.dwMask = CFM_WEIGHT;
1249   cfW.wWeight = 100;
1250   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1251   memset(&cfW, 0, sizeof(cfW));
1252   cfW.cbSize = sizeof(cfW);
1253   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1254   ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1255 
1256   memset(&cfW, 0, sizeof(cfW));
1257   cfW.cbSize = sizeof(cfW);
1258   cfW.dwMask = CFM_SPACING;
1259   cfW.sSpacing = 10;
1260   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1261   memset(&cfW, 0, sizeof(cfW));
1262   cfW.cbSize = sizeof(cfW);
1263   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1264   ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1265 
1266   /* test CFE_UNDERLINE and bUnderlineType interaction */
1267   /* clear bold, italic */
1268   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1269   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1270   cf2.cbSize = sizeof(CHARFORMAT2A);
1271   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1272   cf2.dwEffects = 0;
1273   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1274 
1275   /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1276   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1277   cf2.cbSize = sizeof(CHARFORMAT2A);
1278   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1279   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1280      "got %08x\n", cf2.dwMask);
1281   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1282   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1283 
1284   /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1285   cf2.dwMask = CFM_UNDERLINETYPE;
1286   cf2.bUnderlineType = CFU_UNDERLINE;
1287   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1288   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1289   cf2.cbSize = sizeof(CHARFORMAT2A);
1290   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1291   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1292      "got %08x\n", cf2.dwMask);
1293   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1294   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1295 
1296   /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1297   cf2.dwMask = CFM_UNDERLINETYPE;
1298   cf2.bUnderlineType = CFU_UNDERLINENONE;
1299   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1300   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1301   cf2.cbSize = sizeof(CHARFORMAT2A);
1302   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1303   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1304      "got %08x\n", cf2.dwMask);
1305   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1306   ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1307 
1308   /* another underline type also sets CFE_UNDERLINE */
1309   cf2.dwMask = CFM_UNDERLINETYPE;
1310   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1311   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1312   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1313   cf2.cbSize = sizeof(CHARFORMAT2A);
1314   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1315   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1316      "got %08x\n", cf2.dwMask);
1317   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1318   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1319 
1320   /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1321   cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1322   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1323   cf2.dwEffects = 0;
1324   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1325   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1326   cf2.cbSize = sizeof(CHARFORMAT2A);
1327   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1328   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1329      "got %08x\n", cf2.dwMask);
1330   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1331   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1332 
1333   /* And turing it back on again by just setting CFE_UNDERLINE */
1334   cf2.dwMask = CFM_UNDERLINE;
1335   cf2.dwEffects = CFE_UNDERLINE;
1336   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1337   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1338   cf2.cbSize = sizeof(CHARFORMAT2A);
1339   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1340   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1341      "got %08x\n", cf2.dwMask);
1342   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1343   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1344 
1345   DestroyWindow(hwndRichEdit);
1346 }
1347 
1348 static void test_EM_SETTEXTMODE(void)
1349 {
1350   HWND hwndRichEdit = new_richedit(NULL);
1351   CHARFORMAT2A cf2, cf2test;
1352   CHARRANGE cr;
1353   int rc = 0;
1354 
1355   /*Attempt to use mutually exclusive modes*/
1356   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1357   ok(rc == E_INVALIDARG,
1358      "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1359 
1360   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1361   /*Insert text into the control*/
1362 
1363   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1364 
1365   /*Attempt to change the control to plain text mode*/
1366   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1367   ok(rc == E_UNEXPECTED,
1368      "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1369 
1370   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1371   If rich text is pasted, it should have the same formatting as the rest
1372   of the text in the control*/
1373 
1374   /*Italicize the text
1375   *NOTE: If the default text was already italicized, the test will simply
1376   reverse; in other words, it will copy a regular "wine" into a plain
1377   text window that uses an italicized format*/
1378   cf2.cbSize = sizeof(CHARFORMAT2A);
1379   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1380 
1381   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1382   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1383 
1384   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1385   ok(rc == 0, "Text marked as modified, expected not modified!\n");
1386 
1387   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1388   however, SCF_ALL has been implemented*/
1389   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1390   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1391 
1392   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1393   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1394 
1395   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1396 
1397   /*Select the string "wine"*/
1398   cr.cpMin = 0;
1399   cr.cpMax = 4;
1400   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1401 
1402   /*Copy the italicized "wine" to the clipboard*/
1403   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1404 
1405   /*Reset the formatting to default*/
1406   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1407   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1408   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1409 
1410   /*Clear the text in the control*/
1411   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1412 
1413   /*Switch to Plain Text Mode*/
1414   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1415   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1416 
1417   /*Input "wine" again in normal format*/
1418   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1419 
1420   /*Paste the italicized "wine" into the control*/
1421   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1422 
1423   /*Select a character from the first "wine" string*/
1424   cr.cpMin = 2;
1425   cr.cpMax = 3;
1426   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1427 
1428   /*Retrieve its formatting*/
1429   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1430 
1431   /*Select a character from the second "wine" string*/
1432   cr.cpMin = 5;
1433   cr.cpMax = 6;
1434   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1435 
1436   /*Retrieve its formatting*/
1437   cf2test.cbSize = sizeof(CHARFORMAT2A);
1438   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1439 
1440   /*Compare the two formattings*/
1441     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1442       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1443        cf2.dwEffects, cf2test.dwEffects);
1444   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1445                          printing "wine" in the current format(normal)
1446                          pasting "wine" from the clipboard(italicized)
1447                          comparing the two formats(should differ)*/
1448 
1449   /*Attempt to switch with text in control*/
1450   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1451   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1452 
1453   /*Clear control*/
1454   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1455 
1456   /*Switch into Rich Text mode*/
1457   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1458   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1459 
1460   /*Print "wine" in normal formatting into the control*/
1461   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1462 
1463   /*Paste italicized "wine" into the control*/
1464   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1465 
1466   /*Select text from the first "wine" string*/
1467   cr.cpMin = 1;
1468   cr.cpMax = 3;
1469   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1470 
1471   /*Retrieve its formatting*/
1472   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1473 
1474   /*Select text from the second "wine" string*/
1475   cr.cpMin = 6;
1476   cr.cpMax = 7;
1477   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1478 
1479   /*Retrieve its formatting*/
1480   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1481 
1482   /*Test that the two formattings are not the same*/
1483   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1484       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1485       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1486 
1487   DestroyWindow(hwndRichEdit);
1488 }
1489 
1490 static void test_SETPARAFORMAT(void)
1491 {
1492   HWND hwndRichEdit = new_richedit(NULL);
1493   PARAFORMAT2 fmt;
1494   HRESULT ret;
1495   LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1496   fmt.cbSize = sizeof(PARAFORMAT2);
1497   fmt.dwMask = PFM_ALIGNMENT;
1498   fmt.wAlignment = PFA_LEFT;
1499 
1500   ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1501   ok(ret != 0, "expected non-zero got %d\n", ret);
1502 
1503   fmt.cbSize = sizeof(PARAFORMAT2);
1504   fmt.dwMask = -1;
1505   ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1506   /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1507    * between richedit different native builds of riched20.dll
1508    * used on different Windows versions. */
1509   ret &= ~PFM_TABLEROWDELIMITER;
1510   fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1511 
1512   ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1513   ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1514 
1515   /* Test some other paraformat field defaults */
1516   ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1517   ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1518   ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1519   ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1520 
1521   DestroyWindow(hwndRichEdit);
1522 }
1523 
1524 static void test_TM_PLAINTEXT(void)
1525 {
1526   /*Tests plain text properties*/
1527 
1528   HWND hwndRichEdit = new_richedit(NULL);
1529   CHARFORMAT2A cf2, cf2test;
1530   CHARRANGE cr;
1531   int rc = 0;
1532 
1533   /*Switch to plain text mode*/
1534 
1535   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1536   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1537 
1538   /*Fill control with text*/
1539 
1540   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1541 
1542   /*Select some text and bold it*/
1543 
1544   cr.cpMin = 10;
1545   cr.cpMax = 20;
1546   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1547   cf2.cbSize = sizeof(CHARFORMAT2A);
1548   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1549 
1550   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1551   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1552 
1553   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1554   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1555 
1556   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1557   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1558 
1559   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1560   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1561 
1562   /*Get the formatting of those characters*/
1563 
1564   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1565 
1566   /*Get the formatting of some other characters*/
1567   cf2test.cbSize = sizeof(CHARFORMAT2A);
1568   cr.cpMin = 21;
1569   cr.cpMax = 30;
1570   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1571   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1572 
1573   /*Test that they are the same as plain text allows only one formatting*/
1574 
1575   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1576      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1577      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1578 
1579   /*Fill the control with a "wine" string, which when inserted will be bold*/
1580 
1581   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1582 
1583   /*Copy the bolded "wine" string*/
1584 
1585   cr.cpMin = 0;
1586   cr.cpMax = 4;
1587   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1588   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1589 
1590   /*Swap back to rich text*/
1591 
1592   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1593   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1594 
1595   /*Set the default formatting to bold italics*/
1596 
1597   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1598   cf2.dwMask |= CFM_ITALIC;
1599   cf2.dwEffects ^= CFE_ITALIC;
1600   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1601   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1602 
1603   /*Set the text in the control to "wine", which will be bold and italicized*/
1604 
1605   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1606 
1607   /*Paste the plain text "wine" string, which should take the insert
1608    formatting, which at the moment is bold italics*/
1609 
1610   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1611 
1612   /*Select the first "wine" string and retrieve its formatting*/
1613 
1614   cr.cpMin = 1;
1615   cr.cpMax = 3;
1616   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1617   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1618 
1619   /*Select the second "wine" string and retrieve its formatting*/
1620 
1621   cr.cpMin = 5;
1622   cr.cpMax = 7;
1623   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1624   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1625 
1626   /*Compare the two formattings. They should be the same.*/
1627 
1628   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1629      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1630      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1631   DestroyWindow(hwndRichEdit);
1632 }
1633 
1634 static void test_WM_GETTEXT(void)
1635 {
1636     HWND hwndRichEdit = new_richedit(NULL);
1637     static const char text[] = "Hello. My name is RichEdit!";
1638     static const char text2[] = "Hello. My name is RichEdit!\r";
1639     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1640     char buffer[1024] = {0};
1641     int result;
1642 
1643     /* Baseline test with normal-sized buffer */
1644     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1645     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1646     ok(result == lstrlenA(buffer),
1647         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1648     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1649     result = strcmp(buffer,text);
1650     ok(result == 0,
1651         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1652 
1653     /* Test for returned value of WM_GETTEXTLENGTH */
1654     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1655     ok(result == lstrlenA(text),
1656         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1657         result, lstrlenA(text));
1658 
1659     /* Test for behavior in overflow case */
1660     memset(buffer, 0, 1024);
1661     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1662     ok(result == 0 ||
1663        result == lstrlenA(text) - 1, /* XP, win2k3 */
1664         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1665     result = strcmp(buffer,text);
1666     if (result)
1667         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1668     ok(result == 0,
1669         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1670 
1671     /* Baseline test with normal-sized buffer and carriage return */
1672     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1673     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1674     ok(result == lstrlenA(buffer),
1675         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1676     result = strcmp(buffer,text2_after);
1677     ok(result == 0,
1678         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1679 
1680     /* Test for returned value of WM_GETTEXTLENGTH */
1681     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1682     ok(result == lstrlenA(text2_after),
1683         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1684         result, lstrlenA(text2_after));
1685 
1686     /* Test for behavior of CRLF conversion in case of overflow */
1687     memset(buffer, 0, 1024);
1688     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1689     ok(result == 0 ||
1690        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1691         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1692     result = strcmp(buffer,text2);
1693     if (result)
1694         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1695     ok(result == 0,
1696         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1697 
1698     DestroyWindow(hwndRichEdit);
1699 }
1700 
1701 static void test_EM_GETTEXTRANGE(void)
1702 {
1703     HWND hwndRichEdit = new_richedit(NULL);
1704     const char * text1 = "foo bar\r\nfoo bar";
1705     const char * text2 = "foo bar\rfoo bar";
1706     const char * expect = "bar\rfoo";
1707     char buffer[1024] = {0};
1708     LRESULT result;
1709     TEXTRANGEA textRange;
1710 
1711     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1712 
1713     textRange.lpstrText = buffer;
1714     textRange.chrg.cpMin = 4;
1715     textRange.chrg.cpMax = 11;
1716     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1717     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1718     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1719 
1720     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1721 
1722     textRange.lpstrText = buffer;
1723     textRange.chrg.cpMin = 4;
1724     textRange.chrg.cpMax = 11;
1725     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1726     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1727     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1728 
1729     /* cpMax of text length is used instead of -1 in this case */
1730     textRange.lpstrText = buffer;
1731     textRange.chrg.cpMin = 0;
1732     textRange.chrg.cpMax = -1;
1733     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1734     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1735     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1736 
1737     /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1738     textRange.lpstrText = buffer;
1739     textRange.chrg.cpMin = -1;
1740     textRange.chrg.cpMax = 1;
1741     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1742     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1743     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1744 
1745     /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1746     textRange.lpstrText = buffer;
1747     textRange.chrg.cpMin = 1;
1748     textRange.chrg.cpMax = -1;
1749     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1750     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1751     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1752 
1753     /* no end character is copied if cpMax - cpMin < 0 */
1754     textRange.lpstrText = buffer;
1755     textRange.chrg.cpMin = 5;
1756     textRange.chrg.cpMax = 5;
1757     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1758     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1759     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1760 
1761     /* cpMax of text length is used if cpMax > text length*/
1762     textRange.lpstrText = buffer;
1763     textRange.chrg.cpMin = 0;
1764     textRange.chrg.cpMax = 1000;
1765     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1766     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1767     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1768 
1769     /* Test with multibyte character */
1770     if (!is_lang_japanese)
1771         skip("Skip multibyte character tests on non-Japanese platform\n");
1772     else
1773     {
1774         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1775         textRange.chrg.cpMin = 4;
1776         textRange.chrg.cpMax = 8;
1777         result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1778         todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1779         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1780     }
1781 
1782     DestroyWindow(hwndRichEdit);
1783 }
1784 
1785 static void test_EM_GETSELTEXT(void)
1786 {
1787     HWND hwndRichEdit = new_richedit(NULL);
1788     const char * text1 = "foo bar\r\nfoo bar";
1789     const char * text2 = "foo bar\rfoo bar";
1790     const char * expect = "bar\rfoo";
1791     char buffer[1024] = {0};
1792     LRESULT result;
1793 
1794     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1795 
1796     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1797     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1798     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1799     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1800 
1801     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1802 
1803     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1804     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1805     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1806     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1807 
1808     /* Test with multibyte character */
1809     if (!is_lang_japanese)
1810         skip("Skip multibyte character tests on non-Japanese platform\n");
1811     else
1812     {
1813         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1814         SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1815         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1816         todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1817         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1818     }
1819 
1820     DestroyWindow(hwndRichEdit);
1821 }
1822 
1823 /* FIXME: need to test unimplemented options and robustly test wparam */
1824 static void test_EM_SETOPTIONS(void)
1825 {
1826     HWND hwndRichEdit;
1827     static const char text[] = "Hello. My name is RichEdit!";
1828     char buffer[1024] = {0};
1829     DWORD dwStyle, options, oldOptions;
1830     DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1831                          ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1832                          ES_SELECTIONBAR|ES_VERTICAL;
1833 
1834     /* Test initial options. */
1835     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1836                                 0, 0, 200, 60, NULL, NULL,
1837                                 hmoduleRichEdit, NULL);
1838     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1839        RICHEDIT_CLASS20A, (int) GetLastError());
1840     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1841     ok(options == 0, "Incorrect initial options %x\n", options);
1842     DestroyWindow(hwndRichEdit);
1843 
1844     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1845                                 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1846                                 0, 0, 200, 60, NULL, NULL,
1847                                 hmoduleRichEdit, NULL);
1848     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1849        RICHEDIT_CLASS20A, (int) GetLastError());
1850     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1851     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1852     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1853        "Incorrect initial options %x\n", options);
1854 
1855     /* NEGATIVE TESTING - NO OPTIONS SET */
1856     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1857     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1858 
1859     /* testing no readonly by sending 'a' to the control*/
1860     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1861     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1862     ok(buffer[0]=='a',
1863        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1864     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1865 
1866     /* READONLY - sending 'a' to the control */
1867     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1868     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1869     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1870     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1871     ok(buffer[0]==text[0],
1872        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1873 
1874     /* EM_SETOPTIONS changes the window style, but changing the
1875      * window style does not change the options. */
1876     dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1877     ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1878     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1879     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1880     ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1881     /* Confirm that the text is still read only. */
1882     SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1883     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1884     ok(buffer[0]==text[0],
1885        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1886 
1887     oldOptions = options;
1888     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1889     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1890     ok(options == oldOptions,
1891        "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1892 
1893     DestroyWindow(hwndRichEdit);
1894 }
1895 
1896 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1897 {
1898   CHARFORMAT2A text_format;
1899   text_format.cbSize = sizeof(text_format);
1900   SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1901   SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1902   return (text_format.dwEffects & CFE_LINK) != 0;
1903 }
1904 
1905 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1906 {
1907   BOOL link_present = FALSE;
1908 
1909   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1910   if (is_url)
1911   { /* control text is url; should get CFE_LINK */
1912     ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1913   }
1914   else
1915   {
1916     ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1917   }
1918 }
1919 
1920 static HWND new_static_wnd(HWND parent) {
1921   return new_window("Static", 0, parent);
1922 }
1923 
1924 static void test_EM_AUTOURLDETECT(void)
1925 {
1926   /* DO NOT change the properties of the first two elements. To shorten the
1927      tests, all tests after WM_SETTEXT test just the first two elements -
1928      one non-URL and one URL */
1929   struct urls_s {
1930     const char *text;
1931     BOOL is_url;
1932   } urls[12] = {
1933     {"winehq.org", FALSE},
1934     {"http://www.winehq.org", TRUE},
1935     {"http//winehq.org", FALSE},
1936     {"ww.winehq.org", FALSE},
1937     {"www.winehq.org", TRUE},
1938     {"ftp://192.168.1.1", TRUE},
1939     {"ftp//192.168.1.1", FALSE},
1940     {"mailto:your@email.com", TRUE},
1941     {"prospero:prosperoserver", TRUE},
1942     {"telnet:test", TRUE},
1943     {"news:newserver", TRUE},
1944     {"wais:waisserver", TRUE}
1945   };
1946 
1947   int i, j;
1948   int urlRet=-1;
1949   HWND hwndRichEdit, parent;
1950 
1951   /* All of the following should cause the URL to be detected  */
1952   const char * templates_delim[] = {
1953     "This is some text with X on it",
1954     "This is some text with (X) on it",
1955     "This is some text with X\r on it",
1956     "This is some text with ---X--- on it",
1957     "This is some text with \"X\" on it",
1958     "This is some text with 'X' on it",
1959     "This is some text with 'X' on it",
1960     "This is some text with :X: on it",
1961 
1962     "This text ends with X",
1963 
1964     "This is some text with X) on it",
1965     "This is some text with X--- on it",
1966     "This is some text with X\" on it",
1967     "This is some text with X' on it",
1968     "This is some text with X: on it",
1969 
1970     "This is some text with (X on it",
1971     "This is some text with \rX on it",
1972     "This is some text with ---X on it",
1973     "This is some text with \"X on it",
1974     "This is some text with 'X on it",
1975     "This is some text with :X on it",
1976   };
1977   /* None of these should cause the URL to be detected */
1978   const char * templates_non_delim[] = {
1979     "This is some text with |X| on it",
1980     "This is some text with *X* on it",
1981     "This is some text with /X/ on it",
1982     "This is some text with +X+ on it",
1983     "This is some text with %X% on it",
1984     "This is some text with #X# on it",
1985     "This is some text with @X@ on it",
1986     "This is some text with \\X\\ on it",
1987     "This is some text with |X on it",
1988     "This is some text with *X on it",
1989     "This is some text with /X on it",
1990     "This is some text with +X on it",
1991     "This is some text with %X on it",
1992     "This is some text with #X on it",
1993     "This is some text with @X on it",
1994     "This is some text with \\X on it",
1995     "This is some text with _X on it",
1996   };
1997   /* All of these cause the URL detection to be extended by one more byte,
1998      thus demonstrating that the tested character is considered as part
1999      of the URL. */
2000   const char * templates_xten_delim[] = {
2001     "This is some text with X| on it",
2002     "This is some text with X* on it",
2003     "This is some text with X/ on it",
2004     "This is some text with X+ on it",
2005     "This is some text with X% on it",
2006     "This is some text with X# on it",
2007     "This is some text with X@ on it",
2008     "This is some text with X\\ on it",
2009     "This is some text with X_ on it",
2010   };
2011   /* These delims act as neutral breaks.  Whether the url is ended
2012      or not depends on the next non-neutral character.  We'll test
2013      with Y unchanged, in which case the url should include the
2014      deliminator and the Y.  We'll also test with the Y changed
2015      to a space, in which case the url stops before the
2016      deliminator. */
2017   const char * templates_neutral_delim[] = {
2018     "This is some text with X-Y on it",
2019     "This is some text with X--Y on it",
2020     "This is some text with X!Y on it",
2021     "This is some text with X[Y on it",
2022     "This is some text with X]Y on it",
2023     "This is some text with X{Y on it",
2024     "This is some text with X}Y on it",
2025     "This is some text with X(Y on it",
2026     "This is some text with X)Y on it",
2027     "This is some text with X\"Y on it",
2028     "This is some text with X;Y on it",
2029     "This is some text with X:Y on it",
2030     "This is some text with X'Y on it",
2031     "This is some text with X?Y on it",
2032     "This is some text with X<Y on it",
2033     "This is some text with X>Y on it",
2034     "This is some text with X.Y on it",
2035     "This is some text with X,Y on it",
2036   };
2037   char buffer[1024];
2038 
2039   parent = new_static_wnd(NULL);
2040   hwndRichEdit = new_richedit(parent);
2041   /* Try and pass EM_AUTOURLDETECT some test wParam values */
2042   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2043   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2044   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2045   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2046   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2047   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2048   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2049   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2050   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2051   /* for each url, check the text to see if CFE_LINK effect is present */
2052   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2053 
2054     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2055     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2056     check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2057 
2058     /* Link detection should happen immediately upon WM_SETTEXT */
2059     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2060     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2061     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2062   }
2063   DestroyWindow(hwndRichEdit);
2064 
2065   /* Test detection of URLs within normal text - WM_SETTEXT case. */
2066   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2067     hwndRichEdit = new_richedit(parent);
2068 
2069     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2070       char * at_pos;
2071       int at_offset;
2072       int end_offset;
2073 
2074       at_pos = strchr(templates_delim[j], 'X');
2075       at_offset = at_pos - templates_delim[j];
2076       memcpy(buffer, templates_delim[j], at_offset);
2077       buffer[at_offset] = '\0';
2078       strcat(buffer, urls[i].text);
2079       strcat(buffer, templates_delim[j] + at_offset + 1);
2080       end_offset = at_offset + strlen(urls[i].text);
2081 
2082       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2083       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2084 
2085       /* This assumes no templates start with the URL itself, and that they
2086          have at least two characters before the URL text */
2087       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2088         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2089       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2090         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2091       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2092         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2093 
2094       if (urls[i].is_url)
2095       {
2096         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2097           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2098         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2099           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2100       }
2101       else
2102       {
2103         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2104           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2105         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2106           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2107       }
2108       if (buffer[end_offset] != '\0')
2109       {
2110         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2111           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2112         if (buffer[end_offset +1] != '\0')
2113         {
2114           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2115             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2116         }
2117       }
2118     }
2119 
2120     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
2121       char * at_pos;
2122       int at_offset;
2123       int end_offset;
2124 
2125       at_pos = strchr(templates_non_delim[j], 'X');
2126       at_offset = at_pos - templates_non_delim[j];
2127       memcpy(buffer, templates_non_delim[j], at_offset);
2128       buffer[at_offset] = '\0';
2129       strcat(buffer, urls[i].text);
2130       strcat(buffer, templates_non_delim[j] + at_offset + 1);
2131       end_offset = at_offset + strlen(urls[i].text);
2132 
2133       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2134       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2135 
2136       /* This assumes no templates start with the URL itself, and that they
2137          have at least two characters before the URL text */
2138       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2139         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2140       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2141         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2142       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2143         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2144 
2145       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2146         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2147       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2148         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2149       if (buffer[end_offset] != '\0')
2150       {
2151         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2152           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2153         if (buffer[end_offset +1] != '\0')
2154         {
2155           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2156             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2157         }
2158       }
2159     }
2160 
2161     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
2162       char * at_pos;
2163       int at_offset;
2164       int end_offset;
2165 
2166       at_pos = strchr(templates_xten_delim[j], 'X');
2167       at_offset = at_pos - templates_xten_delim[j];
2168       memcpy(buffer, templates_xten_delim[j], at_offset);
2169       buffer[at_offset] = '\0';
2170       strcat(buffer, urls[i].text);
2171       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2172       end_offset = at_offset + strlen(urls[i].text);
2173 
2174       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2175       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2176 
2177       /* This assumes no templates start with the URL itself, and that they
2178          have at least two characters before the URL text */
2179       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2180         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2181       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2182         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2183       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2184         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2185 
2186       if (urls[i].is_url)
2187       {
2188         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2189           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2190         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2191           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2192         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2193           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2194       }
2195       else
2196       {
2197         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2198           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2199         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2200           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2201         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2202           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2203       }
2204       if (buffer[end_offset +1] != '\0')
2205       {
2206         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2207           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2208         if (buffer[end_offset +2] != '\0')
2209         {
2210           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2211             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2212         }
2213       }
2214     }
2215 
2216     for (j = 0; j < sizeof(templates_neutral_delim) / sizeof(const char *); j++) {
2217       char * at_pos, * end_pos;
2218       int at_offset;
2219       int end_offset;
2220 
2221       if (!urls[i].is_url) continue;
2222 
2223       at_pos = strchr(templates_neutral_delim[j], 'X');
2224       at_offset = at_pos - templates_neutral_delim[j];
2225       memcpy(buffer, templates_neutral_delim[j], at_offset);
2226       buffer[at_offset] = '\0';
2227       strcat(buffer, urls[i].text);
2228       strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2229 
2230       end_pos = strchr(buffer, 'Y');
2231       end_offset = end_pos - buffer;
2232 
2233       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2234       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2235 
2236       /* This assumes no templates start with the URL itself, and that they
2237          have at least two characters before the URL text */
2238       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2239          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2240       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2241          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2242       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2243          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2244 
2245       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2246          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2247       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2248          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2249       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2250          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2251 
2252       *end_pos = ' ';
2253 
2254       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2255       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2256 
2257       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2258          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2259       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2260          "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2261       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2262          "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2263     }
2264 
2265     DestroyWindow(hwndRichEdit);
2266     hwndRichEdit = NULL;
2267   }
2268 
2269   /* Test detection of URLs within normal text - WM_CHAR case. */
2270   /* Test only the first two URL examples for brevity */
2271   for (i = 0; i < 2; i++) {
2272     hwndRichEdit = new_richedit(parent);
2273 
2274     /* Also for brevity, test only the first three delimiters */
2275     for (j = 0; j < 3; j++) {
2276       char * at_pos;
2277       int at_offset;
2278       int end_offset;
2279       int u, v;
2280 
2281       at_pos = strchr(templates_delim[j], 'X');
2282       at_offset = at_pos - templates_delim[j];
2283       end_offset = at_offset + strlen(urls[i].text);
2284 
2285       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2286       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2287       for (u = 0; templates_delim[j][u]; u++) {
2288         if (templates_delim[j][u] == '\r') {
2289           simulate_typing_characters(hwndRichEdit, "\r");
2290         } else if (templates_delim[j][u] != 'X') {
2291           SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2292         } else {
2293           for (v = 0; urls[i].text[v]; v++) {
2294             SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2295           }
2296         }
2297       }
2298       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2299 
2300       /* This assumes no templates start with the URL itself, and that they
2301          have at least two characters before the URL text */
2302       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2303         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2304       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2305         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2306       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2307         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2308 
2309       if (urls[i].is_url)
2310       {
2311         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2312           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2313         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2314           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2315       }
2316       else
2317       {
2318         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2319           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2320         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2321           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2322       }
2323       if (buffer[end_offset] != '\0')
2324       {
2325         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2326           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2327         if (buffer[end_offset +1] != '\0')
2328         {
2329           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2330             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2331         }
2332       }
2333 
2334       /* The following will insert a paragraph break after the first character
2335          of the URL candidate, thus breaking the URL. It is expected that the
2336          CFE_LINK attribute should break across both pieces of the URL */
2337       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2338       simulate_typing_characters(hwndRichEdit, "\r");
2339       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2340 
2341       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2342         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2343       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2344         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2345       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2346         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2347 
2348       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2349         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2350       /* end_offset moved because of paragraph break */
2351       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2352         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2353       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2354       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
2355       {
2356         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2357           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2358         if (buffer[end_offset +2] != '\0')
2359         {
2360           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2361             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2362         }
2363       }
2364 
2365       /* The following will remove the just-inserted paragraph break, thus
2366          restoring the URL */
2367       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2368       simulate_typing_characters(hwndRichEdit, "\b");
2369       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2370 
2371       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2372         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2373       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2374         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2375       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2376         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2377 
2378       if (urls[i].is_url)
2379       {
2380         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2381           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2382         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2383           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2384       }
2385       else
2386       {
2387         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2388           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2389         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2390           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2391       }
2392       if (buffer[end_offset] != '\0')
2393       {
2394         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2395           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2396         if (buffer[end_offset +1] != '\0')
2397         {
2398           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2399             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2400         }
2401       }
2402     }
2403     DestroyWindow(hwndRichEdit);
2404     hwndRichEdit = NULL;
2405   }
2406 
2407   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2408   /* Test just the first two URL examples for brevity */
2409   for (i = 0; i < 2; i++) {
2410     SETTEXTEX st;
2411 
2412     hwndRichEdit = new_richedit(parent);
2413 
2414     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2415        be detected:
2416        1) Set entire text, a la WM_SETTEXT
2417        2) Set a selection of the text to the URL
2418        3) Set a portion of the text at a time, which eventually results in
2419           an URL
2420        All of them should give equivalent results
2421      */
2422 
2423     /* Set entire text in one go, like WM_SETTEXT */
2424     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2425       char * at_pos;
2426       int at_offset;
2427       int end_offset;
2428 
2429       st.codepage = CP_ACP;
2430       st.flags = ST_DEFAULT;
2431 
2432       at_pos = strchr(templates_delim[j], 'X');
2433       at_offset = at_pos - templates_delim[j];
2434       memcpy(buffer, templates_delim[j], at_offset);
2435       buffer[at_offset] = '\0';
2436       strcat(buffer, urls[i].text);
2437       strcat(buffer, templates_delim[j] + at_offset + 1);
2438       end_offset = at_offset + strlen(urls[i].text);
2439 
2440       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2441       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2442 
2443       /* This assumes no templates start with the URL itself, and that they
2444          have at least two characters before the URL text */
2445       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2446         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2447       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2448         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2449       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2450         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2451 
2452       if (urls[i].is_url)
2453       {
2454         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2455           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2456         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2457           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2458       }
2459       else
2460       {
2461         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2462           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2463         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2464           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2465       }
2466       if (buffer[end_offset] != '\0')
2467       {
2468         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2469           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2470         if (buffer[end_offset +1] != '\0')
2471         {
2472           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2473             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2474         }
2475       }
2476     }
2477 
2478     /* Set selection with X to the URL */
2479     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2480       char * at_pos;
2481       int at_offset;
2482       int end_offset;
2483 
2484       at_pos = strchr(templates_delim[j], 'X');
2485       at_offset = at_pos - templates_delim[j];
2486       end_offset = at_offset + strlen(urls[i].text);
2487 
2488       st.codepage = CP_ACP;
2489       st.flags = ST_DEFAULT;
2490       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2491       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2492       st.flags = ST_SELECTION;
2493       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2494       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2495       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2496 
2497       /* This assumes no templates start with the URL itself, and that they
2498          have at least two characters before the URL text */
2499       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2500         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2501       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2502         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2503       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2504         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2505 
2506       if (urls[i].is_url)
2507       {
2508         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2509           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2510         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2511           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2512       }
2513       else
2514       {
2515         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2516           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2517         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2518           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2519       }
2520       if (buffer[end_offset] != '\0')
2521       {
2522         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2523           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2524         if (buffer[end_offset +1] != '\0')
2525         {
2526           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2527             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2528         }
2529       }
2530     }
2531 
2532     /* Set selection with X to the first character of the URL, then the rest */
2533     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2534       char * at_pos;
2535       int at_offset;
2536       int end_offset;
2537 
2538       at_pos = strchr(templates_delim[j], 'X');
2539       at_offset = at_pos - templates_delim[j];
2540       end_offset = at_offset + strlen(urls[i].text);
2541 
2542       strcpy(buffer, "YY");
2543       buffer[0] = urls[i].text[0];
2544 
2545       st.codepage = CP_ACP;
2546       st.flags = ST_DEFAULT;
2547       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2548       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2549       st.flags = ST_SELECTION;
2550       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2551       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2552       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2553       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2554       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2555 
2556       /* This assumes no templates start with the URL itself, and that they
2557          have at least two characters before the URL text */
2558       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2559         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2560       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2561         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2562       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2563         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2564 
2565       if (urls[i].is_url)
2566       {
2567         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2568           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2569         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2570           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2571       }
2572       else
2573       {
2574         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2575           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2576         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2577           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2578       }
2579       if (buffer[end_offset] != '\0')
2580       {
2581         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2582           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2583         if (buffer[end_offset +1] != '\0')
2584         {
2585           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2586             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2587         }
2588       }
2589     }
2590 
2591     DestroyWindow(hwndRichEdit);
2592     hwndRichEdit = NULL;
2593   }
2594 
2595   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2596   /* Test just the first two URL examples for brevity */
2597   for (i = 0; i < 2; i++) {
2598     hwndRichEdit = new_richedit(parent);
2599 
2600     /* Set selection with X to the URL */
2601     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2602       char * at_pos;
2603       int at_offset;
2604       int end_offset;
2605 
2606       at_pos = strchr(templates_delim[j], 'X');
2607       at_offset = at_pos - templates_delim[j];
2608       end_offset = at_offset + strlen(urls[i].text);
2609 
2610       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2611       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2612       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2613       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2614       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2615 
2616       /* This assumes no templates start with the URL itself, and that they
2617          have at least two characters before the URL text */
2618       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2619         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2620       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2621         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2622       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2623         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2624 
2625       if (urls[i].is_url)
2626       {
2627         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2628           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2629         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2630           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2631       }
2632       else
2633       {
2634         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2635           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2636         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2637           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2638       }
2639       if (buffer[end_offset] != '\0')
2640       {
2641         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2642           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2643         if (buffer[end_offset +1] != '\0')
2644         {
2645           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2646             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2647         }
2648       }
2649     }
2650 
2651     /* Set selection with X to the first character of the URL, then the rest */
2652     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2653       char * at_pos;
2654       int at_offset;
2655       int end_offset;
2656 
2657       at_pos = strchr(templates_delim[j], 'X');
2658       at_offset = at_pos - templates_delim[j];
2659       end_offset = at_offset + strlen(urls[i].text);
2660 
2661       strcpy(buffer, "YY");
2662       buffer[0] = urls[i].text[0];
2663 
2664       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2665       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2666       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2667       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2668       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2669       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2670       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2671 
2672       /* This assumes no templates start with the URL itself, and that they
2673          have at least two characters before the URL text */
2674       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2675         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2676       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2677         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2678       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2679         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2680 
2681       if (urls[i].is_url)
2682       {
2683         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2684           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2685         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2686           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2687       }
2688       else
2689       {
2690         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2691           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2692         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2693           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2694       }
2695       if (buffer[end_offset] != '\0')
2696       {
2697         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2698           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2699         if (buffer[end_offset +1] != '\0')
2700         {
2701           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2702             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2703         }
2704       }
2705     }
2706 
2707     DestroyWindow(hwndRichEdit);
2708     hwndRichEdit = NULL;
2709   }
2710 
2711   DestroyWindow(parent);
2712 }
2713 
2714 static void test_EM_SCROLL(void)
2715 {
2716   int i, j;
2717   int r; /* return value */
2718   int expr; /* expected return value */
2719   HWND hwndRichEdit = new_richedit(NULL);
2720   int y_before, y_after; /* units of lines of text */
2721 
2722   /* test a richedit box containing a single line of text */
2723   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2724   expr = 0x00010000;
2725   for (i = 0; i < 4; i++) {
2726     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2727 
2728     r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2729     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2730     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2731        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2732     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2733        "(i == %d)\n", y_after, i);
2734   }
2735 
2736   /*
2737    * test a richedit box that will scroll. There are two general
2738    * cases: the case without any long lines and the case with a long
2739    * line.
2740    */
2741   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2742     if (i == 0)
2743       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2744     else
2745       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2746                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2747                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2748                   "LONG LINE \nb\nc\nd\ne");
2749     for (j = 0; j < 12; j++) /* reset scroll position to top */
2750       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2751 
2752     /* get first visible line */
2753     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2754     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2755 
2756     /* get new current first visible line */
2757     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2758 
2759     ok(((r & 0xffffff00) == 0x00010000) &&
2760        ((r & 0x000000ff) != 0x00000000),
2761        "EM_SCROLL page down didn't scroll by a small positive number of "
2762        "lines (r == 0x%08x)\n", r);
2763     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2764        "(line %d scrolled to line %d\n", y_before, y_after);
2765 
2766     y_before = y_after;
2767 
2768     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2769     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2770     ok(((r & 0xffffff00) == 0x0001ff00),
2771        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2772        "(r == 0x%08x)\n", r);
2773     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2774        "%d scrolled to line %d\n", y_before, y_after);
2775 
2776     y_before = y_after;
2777 
2778     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2779 
2780     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2781 
2782     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2783        "(r == 0x%08x)\n", r);
2784     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2785        "1 line (%d scrolled to %d)\n", y_before, y_after);
2786 
2787     y_before = y_after;
2788 
2789     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2790 
2791     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2792 
2793     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2794        "(r == 0x%08x)\n", r);
2795     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2796        "line (%d scrolled to %d)\n", y_before, y_after);
2797 
2798     y_before = y_after;
2799 
2800     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2801                     SB_LINEUP, 0); /* lineup beyond top */
2802 
2803     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2804 
2805     ok(r == 0x00010000,
2806        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2807     ok(y_before == y_after,
2808        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2809 
2810     y_before = y_after;
2811 
2812     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2813                     SB_PAGEUP, 0);/*page up beyond top */
2814 
2815     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2816 
2817     ok(r == 0x00010000,
2818        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2819     ok(y_before == y_after,
2820        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2821 
2822     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2823       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2824     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2825     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2826                     SB_PAGEDOWN, 0); /* page down beyond bot */
2827     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2828 
2829     ok(r == 0x00010000,
2830        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2831     ok(y_before == y_after,
2832        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2833        y_before, y_after);
2834 
2835     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2836     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2837     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2838 
2839     ok(r == 0x00010000,
2840        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2841     ok(y_before == y_after,
2842        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2843        y_before, y_after);
2844   }
2845   DestroyWindow(hwndRichEdit);
2846 }
2847 
2848 static unsigned int recursionLevel = 0;
2849 static unsigned int WM_SIZE_recursionLevel = 0;
2850 static BOOL bailedOutOfRecursion = FALSE;
2851 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2852 
2853 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2854 {
2855     LRESULT r;
2856 
2857     if (bailedOutOfRecursion) return 0;
2858     if (recursionLevel >= 32) {
2859         bailedOutOfRecursion = TRUE;
2860         return 0;
2861     }
2862 
2863     recursionLevel++;
2864     switch (message) {
2865     case WM_SIZE:
2866         WM_SIZE_recursionLevel++;
2867         r = richeditProc(hwnd, message, wParam, lParam);
2868         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2869         ShowScrollBar(hwnd, SB_VERT, TRUE);
2870         WM_SIZE_recursionLevel--;
2871         break;
2872     default:
2873         r = richeditProc(hwnd, message, wParam, lParam);
2874         break;
2875     }
2876     recursionLevel--;
2877     return r;
2878 }
2879 
2880 static void test_scrollbar_visibility(void)
2881 {
2882   HWND hwndRichEdit;
2883   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2884   SCROLLINFO si;
2885   WNDCLASSA cls;
2886   BOOL r;
2887 
2888   /* These tests show that richedit should temporarily refrain from automatically
2889      hiding or showing its scrollbars (vertical at least) when an explicit request
2890      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2891      Some applications depend on forced showing (when otherwise richedit would
2892      hide the vertical scrollbar) and are thrown on an endless recursive loop
2893      if richedit auto-hides the scrollbar again. Apparently they never heard of
2894      the ES_DISABLENOSCROLL style... */
2895 
2896   hwndRichEdit = new_richedit(NULL);
2897 
2898   /* Test default scrollbar visibility behavior */
2899   memset(&si, 0, sizeof(si));
2900   si.cbSize = sizeof(si);
2901   si.fMask = SIF_PAGE | SIF_RANGE;
2902   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2904     "Vertical scrollbar is visible, should be invisible.\n");
2905   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2906         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2907         si.nPage, si.nMin, si.nMax);
2908 
2909   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2910   memset(&si, 0, sizeof(si));
2911   si.cbSize = sizeof(si);
2912   si.fMask = SIF_PAGE | SIF_RANGE;
2913   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915     "Vertical scrollbar is visible, should be invisible.\n");
2916   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2917         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2918         si.nPage, si.nMin, si.nMax);
2919 
2920   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2921   memset(&si, 0, sizeof(si));
2922   si.cbSize = sizeof(si);
2923   si.fMask = SIF_PAGE | SIF_RANGE;
2924   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2926     "Vertical scrollbar is invisible, should be visible.\n");
2927   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2929         si.nPage, si.nMin, si.nMax);
2930 
2931   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2932      even though it hides the scrollbar */
2933   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2934   memset(&si, 0, sizeof(si));
2935   si.cbSize = sizeof(si);
2936   si.fMask = SIF_PAGE | SIF_RANGE;
2937   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2938   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2939     "Vertical scrollbar is visible, should be invisible.\n");
2940   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2941         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2942         si.nPage, si.nMin, si.nMax);
2943 
2944   /* Setting non-scrolling text again does *not* reset scrollbar range */
2945   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2946   memset(&si, 0, sizeof(si));
2947   si.cbSize = sizeof(si);
2948   si.fMask = SIF_PAGE | SIF_RANGE;
2949   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2950   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2951     "Vertical scrollbar is visible, should be invisible.\n");
2952   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2953         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2954         si.nPage, si.nMin, si.nMax);
2955 
2956   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2957   memset(&si, 0, sizeof(si));
2958   si.cbSize = sizeof(si);
2959   si.fMask = SIF_PAGE | SIF_RANGE;
2960   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2962     "Vertical scrollbar is visible, should be invisible.\n");
2963   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2964         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2965         si.nPage, si.nMin, si.nMax);
2966 
2967   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2968   memset(&si, 0, sizeof(si));
2969   si.cbSize = sizeof(si);
2970   si.fMask = SIF_PAGE | SIF_RANGE;
2971   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2973     "Vertical scrollbar is visible, should be invisible.\n");
2974   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2975         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2976         si.nPage, si.nMin, si.nMax);
2977 
2978   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2979   memset(&si, 0, sizeof(si));
2980   si.cbSize = sizeof(si);
2981   si.fMask = SIF_PAGE | SIF_RANGE;
2982   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2984     "Vertical scrollbar is visible, should be invisible.\n");
2985   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2987         si.nPage, si.nMin, si.nMax);
2988 
2989   DestroyWindow(hwndRichEdit);
2990 
2991   /* Test again, with ES_DISABLENOSCROLL style */
2992   hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2993 
2994   /* Test default scrollbar visibility behavior */
2995   memset(&si, 0, sizeof(si));
2996   si.cbSize = sizeof(si);
2997   si.fMask = SIF_PAGE | SIF_RANGE;
2998   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2999   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3000     "Vertical scrollbar is invisible, should be visible.\n");
3001   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3002         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3003         si.nPage, si.nMin, si.nMax);
3004 
3005   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3006   memset(&si, 0, sizeof(si));
3007   si.cbSize = sizeof(si);
3008   si.fMask = SIF_PAGE | SIF_RANGE;
3009   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3010   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3011     "Vertical scrollbar is invisible, should be visible.\n");
3012   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3013         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3014         si.nPage, si.nMin, si.nMax);
3015 
3016   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3017   memset(&si, 0, sizeof(si));
3018   si.cbSize = sizeof(si);
3019   si.fMask = SIF_PAGE | SIF_RANGE;
3020   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3021   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3022     "Vertical scrollbar is invisible, should be visible.\n");
3023   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3024         "reported page/range is %d (%d..%d)\n",
3025         si.nPage, si.nMin, si.nMax);
3026 
3027   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3028   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3029   memset(&si, 0, sizeof(si));
3030   si.cbSize = sizeof(si);
3031   si.fMask = SIF_PAGE | SIF_RANGE;
3032   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3033   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3034     "Vertical scrollbar is invisible, should be visible.\n");
3035   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3036         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3037         si.nPage, si.nMin, si.nMax);
3038 
3039   /* Setting non-scrolling text again does *not* reset scrollbar range */
3040   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3041   memset(&si, 0, sizeof(si));
3042   si.cbSize = sizeof(si);
3043   si.fMask = SIF_PAGE | SIF_RANGE;
3044   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3045   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3046     "Vertical scrollbar is invisible, should be visible.\n");
3047   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3048         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3049         si.nPage, si.nMin, si.nMax);
3050 
3051   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3052   memset(&si, 0, sizeof(si));
3053   si.cbSize = sizeof(si);
3054   si.fMask = SIF_PAGE | SIF_RANGE;
3055   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3056   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3057     "Vertical scrollbar is invisible, should be visible.\n");
3058   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3059         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3060         si.nPage, si.nMin, si.nMax);
3061 
3062   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3063   memset(&si, 0, sizeof(si));
3064   si.cbSize = sizeof(si);
3065   si.fMask = SIF_PAGE | SIF_RANGE;
3066   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3067   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3068     "Vertical scrollbar is invisible, should be visible.\n");
3069   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3070         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3071         si.nPage, si.nMin, si.nMax);
3072 
3073   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3074   memset(&si, 0, sizeof(si));
3075   si.cbSize = sizeof(si);
3076   si.fMask = SIF_PAGE | SIF_RANGE;
3077   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3078   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3079     "Vertical scrollbar is invisible, should be visible.\n");
3080   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3081         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3082         si.nPage, si.nMin, si.nMax);
3083 
3084   DestroyWindow(hwndRichEdit);
3085 
3086   /* Test behavior with explicit visibility request, using ShowScrollBar() */
3087   hwndRichEdit = new_richedit(NULL);
3088 
3089   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3090   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3091   memset(&si, 0, sizeof(si));
3092   si.cbSize = sizeof(si);
3093   si.fMask = SIF_PAGE | SIF_RANGE;
3094   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3095   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3096     "Vertical scrollbar is invisible, should be visible.\n");
3097   todo_wine {
3098   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3099         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3100         si.nPage, si.nMin, si.nMax);
3101   }
3102 
3103   /* Ditto, see above */
3104   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3105   memset(&si, 0, sizeof(si));
3106   si.cbSize = sizeof(si);
3107   si.fMask = SIF_PAGE | SIF_RANGE;
3108   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3109   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3110     "Vertical scrollbar is invisible, should be visible.\n");
3111   todo_wine {
3112   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3113         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3114         si.nPage, si.nMin, si.nMax);
3115   }
3116 
3117   /* Ditto, see above */
3118   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3119   memset(&si, 0, sizeof(si));
3120   si.cbSize = sizeof(si);
3121   si.fMask = SIF_PAGE | SIF_RANGE;
3122   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124     "Vertical scrollbar is invisible, should be visible.\n");
3125   todo_wine {
3126   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3127         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3128         si.nPage, si.nMin, si.nMax);
3129   }
3130 
3131   /* Ditto, see above */
3132   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3133   memset(&si, 0, sizeof(si));
3134   si.cbSize = sizeof(si);
3135   si.fMask = SIF_PAGE | SIF_RANGE;
3136   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3137   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3138     "Vertical scrollbar is invisible, should be visible.\n");
3139   todo_wine {
3140   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3141         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3142         si.nPage, si.nMin, si.nMax);
3143   }
3144 
3145   /* Ditto, see above */
3146   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3147   memset(&si, 0, sizeof(si));
3148   si.cbSize = sizeof(si);
3149   si.fMask = SIF_PAGE | SIF_RANGE;
3150   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3151   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3152     "Vertical scrollbar is invisible, should be visible.\n");
3153   todo_wine {
3154   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3155         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3156         si.nPage, si.nMin, si.nMax);
3157   }
3158 
3159   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3160   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3161   memset(&si, 0, sizeof(si));
3162   si.cbSize = sizeof(si);
3163   si.fMask = SIF_PAGE | SIF_RANGE;
3164   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3165   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3166     "Vertical scrollbar is visible, should be invisible.\n");
3167   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3168         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3169         si.nPage, si.nMin, si.nMax);
3170 
3171   DestroyWindow(hwndRichEdit);
3172 
3173   hwndRichEdit = new_richedit(NULL);
3174 
3175   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3176   memset(&si, 0, sizeof(si));
3177   si.cbSize = sizeof(si);
3178   si.fMask = SIF_PAGE | SIF_RANGE;
3179   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3180   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3181     "Vertical scrollbar is visible, should be invisible.\n");
3182   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3183         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3184         si.nPage, si.nMin, si.nMax);
3185 
3186   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3187   memset(&si, 0, sizeof(si));
3188   si.cbSize = sizeof(si);
3189   si.fMask = SIF_PAGE | SIF_RANGE;
3190   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3191   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3192     "Vertical scrollbar is visible, should be invisible.\n");
3193   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3194         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3195         si.nPage, si.nMin, si.nMax);
3196 
3197   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3198   memset(&si, 0, sizeof(si));
3199   si.cbSize = sizeof(si);
3200   si.fMask = SIF_PAGE | SIF_RANGE;
3201   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3202   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3203     "Vertical scrollbar is visible, should be invisible.\n");
3204   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3205         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3206         si.nPage, si.nMin, si.nMax);
3207 
3208   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3209   memset(&si, 0, sizeof(si));
3210   si.cbSize = sizeof(si);
3211   si.fMask = SIF_PAGE | SIF_RANGE;
3212   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3213   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3214     "Vertical scrollbar is visible, should be invisible.\n");
3215   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3216         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3217         si.nPage, si.nMin, si.nMax);
3218 
3219   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3220   memset(&si, 0, sizeof(si));
3221   si.cbSize = sizeof(si);
3222   si.fMask = SIF_PAGE | SIF_RANGE;
3223   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3224   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3225     "Vertical scrollbar is invisible, should be visible.\n");
3226   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3227         "reported page/range is %d (%d..%d)\n",
3228         si.nPage, si.nMin, si.nMax);
3229 
3230   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3231   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3232   memset(&si, 0, sizeof(si));
3233   si.cbSize = sizeof(si);
3234   si.fMask = SIF_PAGE | SIF_RANGE;
3235   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3236   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3237     "Vertical scrollbar is visible, should be invisible.\n");
3238   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3239         "reported page/range is %d (%d..%d)\n",
3240         si.nPage, si.nMin, si.nMax);
3241 
3242   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3243   memset(&si, 0, sizeof(si));
3244   si.cbSize = sizeof(si);
3245   si.fMask = SIF_PAGE | SIF_RANGE;
3246   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3247   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3248     "Vertical scrollbar is visible, should be invisible.\n");
3249   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3250         "reported page/range is %d (%d..%d)\n",
3251         si.nPage, si.nMin, si.nMax);
3252 
3253   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3254      EM_SCROLL will make visible any forcefully invisible scrollbar */
3255   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3256   memset(&si, 0, sizeof(si));
3257   si.cbSize = sizeof(si);
3258   si.fMask = SIF_PAGE | SIF_RANGE;
3259   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3260   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3261     "Vertical scrollbar is invisible, should be visible.\n");
3262   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3263         "reported page/range is %d (%d..%d)\n",
3264         si.nPage, si.nMin, si.nMax);
3265 
3266   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3267   memset(&si, 0, sizeof(si));
3268   si.cbSize = sizeof(si);
3269   si.fMask = SIF_PAGE | SIF_RANGE;
3270   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3271   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3272     "Vertical scrollbar is visible, should be invisible.\n");
3273   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3274         "reported page/range is %d (%d..%d)\n",
3275         si.nPage, si.nMin, si.nMax);
3276 
3277   /* Again, EM_SCROLL, with SB_LINEUP */
3278   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3279   memset(&si, 0, sizeof(si));
3280   si.cbSize = sizeof(si);
3281   si.fMask = SIF_PAGE | SIF_RANGE;
3282   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3283   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3284     "Vertical scrollbar is invisible, should be visible.\n");
3285   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3286         "reported page/range is %d (%d..%d)\n",
3287         si.nPage, si.nMin, si.nMax);
3288 
3289   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3290   memset(&si, 0, sizeof(si));
3291   si.cbSize = sizeof(si);
3292   si.fMask = SIF_PAGE | SIF_RANGE;
3293   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3294   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3295     "Vertical scrollbar is visible, should be invisible.\n");
3296   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3297         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3298         si.nPage, si.nMin, si.nMax);
3299 
3300   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3301   memset(&si, 0, sizeof(si));
3302   si.cbSize = sizeof(si);
3303   si.fMask = SIF_PAGE | SIF_RANGE;
3304   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3305   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3306     "Vertical scrollbar is invisible, should be visible.\n");
3307   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3308         "reported page/range is %d (%d..%d)\n",
3309         si.nPage, si.nMin, si.nMax);
3310 
3311   DestroyWindow(hwndRichEdit);
3312 
3313 
3314   /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3315   hwndRichEdit = new_richedit(NULL);
3316 
3317 #define ENABLE_WS_VSCROLL(hwnd) \
3318     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3319 #define DISABLE_WS_VSCROLL(hwnd) \
3320     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3321 
3322   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3323   ENABLE_WS_VSCROLL(hwndRichEdit);
3324   memset(&si, 0, sizeof(si));
3325   si.cbSize = sizeof(si);
3326   si.fMask = SIF_PAGE | SIF_RANGE;
3327   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3328   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3329     "Vertical scrollbar is invisible, should be visible.\n");
3330   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3331         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3332         si.nPage, si.nMin, si.nMax);
3333 
3334   /* Ditto, see above */
3335   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3336   memset(&si, 0, sizeof(si));
3337   si.cbSize = sizeof(si);
3338   si.fMask = SIF_PAGE | SIF_RANGE;
3339   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3340   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3341     "Vertical scrollbar is invisible, should be visible.\n");
3342   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3343         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3344         si.nPage, si.nMin, si.nMax);
3345 
3346   /* Ditto, see above */
3347   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3348   memset(&si, 0, sizeof(si));
3349   si.cbSize = sizeof(si);
3350   si.fMask = SIF_PAGE | SIF_RANGE;
3351   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3352   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3353     "Vertical scrollbar is invisible, should be visible.\n");
3354   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3355         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3356         si.nPage, si.nMin, si.nMax);
3357 
3358   /* Ditto, see above */
3359   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3360   memset(&si, 0, sizeof(si));
3361   si.cbSize = sizeof(si);
3362   si.fMask = SIF_PAGE | SIF_RANGE;
3363   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3364   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3365     "Vertical scrollbar is invisible, should be visible.\n");
3366   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3367         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3368         si.nPage, si.nMin, si.nMax);
3369 
3370   /* Ditto, see above */
3371   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3372   memset(&si, 0, sizeof(si));
3373   si.cbSize = sizeof(si);
3374   si.fMask = SIF_PAGE | SIF_RANGE;
3375   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3376   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3377     "Vertical scrollbar is invisible, should be visible.\n");
3378   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3379         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3380         si.nPage, si.nMin, si.nMax);
3381 
3382   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3383   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3384   memset(&si, 0, sizeof(si));
3385   si.cbSize = sizeof(si);
3386   si.fMask = SIF_PAGE | SIF_RANGE;
3387   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3388   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3389     "Vertical scrollbar is visible, should be invisible.\n");
3390   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3391         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3392         si.nPage, si.nMin, si.nMax);
3393 
3394   DestroyWindow(hwndRichEdit);
3395 
3396   hwndRichEdit = new_richedit(NULL);
3397 
3398   DISABLE_WS_VSCROLL(hwndRichEdit);
3399   memset(&si, 0, sizeof(si));
3400   si.cbSize = sizeof(si);
3401   si.fMask = SIF_PAGE | SIF_RANGE;
3402   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3403   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3404     "Vertical scrollbar is visible, should be invisible.\n");
3405   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3406         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3407         si.nPage, si.nMin, si.nMax);
3408 
3409   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3410   memset(&si, 0, sizeof(si));
3411   si.cbSize = sizeof(si);
3412   si.fMask = SIF_PAGE | SIF_RANGE;
3413   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3414   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3415     "Vertical scrollbar is visible, should be invisible.\n");
3416   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3417         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3418         si.nPage, si.nMin, si.nMax);
3419 
3420   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3421   memset(&si, 0, sizeof(si));
3422   si.cbSize = sizeof(si);
3423   si.fMask = SIF_PAGE | SIF_RANGE;
3424   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3425   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3426     "Vertical scrollbar is visible, should be invisible.\n");
3427   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3428         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3429         si.nPage, si.nMin, si.nMax);
3430 
3431   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3432   memset(&si, 0, sizeof(si));
3433   si.cbSize = sizeof(si);
3434   si.fMask = SIF_PAGE | SIF_RANGE;
3435   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3436   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3437     "Vertical scrollbar is visible, should be invisible.\n");
3438   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3439         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3440         si.nPage, si.nMin, si.nMax);
3441 
3442   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3443   memset(&si, 0, sizeof(si));
3444   si.cbSize = sizeof(si);
3445   si.fMask = SIF_PAGE | SIF_RANGE;
3446   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3447   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3448     "Vertical scrollbar is invisible, should be visible.\n");
3449   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3450         "reported page/range is %d (%d..%d)\n",
3451         si.nPage, si.nMin, si.nMax);
3452 
3453   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3454   DISABLE_WS_VSCROLL(hwndRichEdit);
3455   memset(&si, 0, sizeof(si));
3456   si.cbSize = sizeof(si);
3457   si.fMask = SIF_PAGE | SIF_RANGE;
3458   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3459   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3460     "Vertical scrollbar is visible, should be invisible.\n");
3461   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3462         "reported page/range is %d (%d..%d)\n",
3463         si.nPage, si.nMin, si.nMax);
3464 
3465   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3466   memset(&si, 0, sizeof(si));
3467   si.cbSize = sizeof(si);
3468   si.fMask = SIF_PAGE | SIF_RANGE;
3469   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3470   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3471     "Vertical scrollbar is visible, should be invisible.\n");
3472   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3473         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3474         si.nPage, si.nMin, si.nMax);
3475 
3476   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3477   memset(&si, 0, sizeof(si));
3478   si.cbSize = sizeof(si);
3479   si.fMask = SIF_PAGE | SIF_RANGE;
3480   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3481   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3482     "Vertical scrollbar is invisible, should be visible.\n");
3483   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3484         "reported page/range is %d (%d..%d)\n",
3485         si.nPage, si.nMin, si.nMax);
3486 
3487   DISABLE_WS_VSCROLL(hwndRichEdit);
3488   memset(&si, 0, sizeof(si));
3489   si.cbSize = sizeof(si);
3490   si.fMask = SIF_PAGE | SIF_RANGE;
3491   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3492   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3493     "Vertical scrollbar is visible, should be invisible.\n");
3494   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3495         "reported page/range is %d (%d..%d)\n",
3496         si.nPage, si.nMin, si.nMax);
3497 
3498   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3499      EM_SCROLL will make visible any forcefully invisible scrollbar */
3500   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3501   memset(&si, 0, sizeof(si));
3502   si.cbSize = sizeof(si);
3503   si.fMask = SIF_PAGE | SIF_RANGE;
3504   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3505   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3506     "Vertical scrollbar is invisible, should be visible.\n");
3507   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3508         "reported page/range is %d (%d..%d)\n",
3509         si.nPage, si.nMin, si.nMax);
3510 
3511   DISABLE_WS_VSCROLL(hwndRichEdit);
3512   memset(&si, 0, sizeof(si));
3513   si.cbSize = sizeof(si);
3514   si.fMask = SIF_PAGE | SIF_RANGE;
3515   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3516   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3517     "Vertical scrollbar is visible, should be invisible.\n");
3518   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3519         "reported page/range is %d (%d..%d)\n",
3520         si.nPage, si.nMin, si.nMax);
3521 
3522   /* Again, EM_SCROLL, with SB_LINEUP */
3523   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3524   memset(&si, 0, sizeof(si));
3525   si.cbSize = sizeof(si);
3526   si.fMask = SIF_PAGE | SIF_RANGE;
3527   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3528   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3529     "Vertical scrollbar is invisible, should be visible.\n");
3530   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3531         "reported page/range is %d (%d..%d)\n",
3532         si.nPage, si.nMin, si.nMax);
3533 
3534   DestroyWindow(hwndRichEdit);
3535 
3536   /* This window proc models what is going on with Corman Lisp 3.0.
3537      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3538      force the scrollbar into visibility. Recursion should NOT happen
3539      as a result of this action.
3540    */
3541   r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3542   if (r) {
3543     richeditProc = cls.lpfnWndProc;
3544     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3545     cls.lpszClassName = "RicheditStupidOverride";
3546     if(!RegisterClassA(&cls)) assert(0);
3547 
3548     recursionLevel = 0;
3549     WM_SIZE_recursionLevel = 0;
3550     bailedOutOfRecursion = FALSE;
3551     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3552     ok(!bailedOutOfRecursion,
3553         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3554 
3555     recursionLevel = 0;
3556     WM_SIZE_recursionLevel = 0;
3557     bailedOutOfRecursion = FALSE;
3558     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3559     ok(!bailedOutOfRecursion,
3560         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3561 
3562     /* Unblock window in order to process WM_DESTROY */
3563     recursionLevel = 0;
3564     bailedOutOfRecursion = FALSE;
3565     WM_SIZE_recursionLevel = 0;
3566     DestroyWindow(hwndRichEdit);
3567   }
3568 }
3569 
3570 static void test_EM_SETUNDOLIMIT(void)
3571 {
3572   /* cases we test for:
3573    * default behaviour - limiting at 100 undo's
3574    * undo disabled - setting a limit of 0
3575    * undo limited -  undo limit set to some to some number, like 2
3576    * bad input - sending a negative number should default to 100 undo's */
3577 
3578   HWND hwndRichEdit = new_richedit(NULL);
3579   CHARRANGE cr;
3580   int i;
3581   int result;
3582 
3583   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3584   cr.cpMin = 0;
3585   cr.cpMax = -1;
3586   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3587 
3588   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3589     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3590       also, multiple pastes don't combine like WM_CHAR would */
3591 
3592   /* first case - check the default */
3593   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3594   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3595     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3596   for (i=0; i<100; i++) /* Undo 100 of them */
3597     SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3598   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3599      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3600 
3601   /* second case - cannot undo */
3602   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3603   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3604   SendMessageA(hwndRichEdit,
3605               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3606   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3607      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3608 
3609   /* third case - set it to an arbitrary number */
3610   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3611   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3612   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3613   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3614   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3615   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3616   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3617      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3618   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3619   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3620      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3621   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3622   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3623      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3624 
3625   /* fourth case - setting negative numbers should default to 100 undos */
3626   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3627   result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3628   ok (result == 100,
3629       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3630 
3631   DestroyWindow(hwndRichEdit);
3632 }
3633 
3634 static void test_ES_PASSWORD(void)
3635 {
3636   /* This isn't hugely testable, so we're just going to run it through its paces */
3637 
3638   HWND hwndRichEdit = new_richedit(NULL);
3639   WCHAR result;
3640 
3641   /* First, check the default of a regular control */
3642   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3643   ok (result == 0,
3644 	"EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3645 
3646   /* Now, set it to something normal */
3647   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3648   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3649   ok (result == 120,
3650 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3651 
3652   /* Now, set it to something odd */
3653   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3654   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3655   ok (result == 1234,
3656 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3657   DestroyWindow(hwndRichEdit);
3658 }
3659 
3660 LONG streamout_written = 0;
3661 
3662 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3663                                          LPBYTE pbBuff,
3664                                          LONG cb,
3665                                          LONG *pcb)
3666 {
3667   char** str = (char**)dwCookie;
3668   *pcb = cb;
3669   if (*pcb > 0) {
3670     memcpy(*str, pbBuff, *pcb);
3671     *str += *pcb;
3672   }
3673   streamout_written = *pcb;
3674   return 0;
3675 }
3676 
3677 static void test_WM_SETTEXT(void)
3678 {
3679   HWND hwndRichEdit = new_richedit(NULL);
3680   const char * TestItem1 = "TestSomeText";
3681   const char * TestItem2 = "TestSomeText\r";
3682   const char * TestItem2_after = "TestSomeText\r\n";
3683   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3684   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3685   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3686   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3687   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3688   const char * TestItem5_after = "TestSomeText TestSomeText";
3689   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3690   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3691   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3692   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3693 
3694   const char rtftextA[] = "{\\rtf sometext}";
3695   const char urtftextA[] = "{\\urtf sometext}";
3696   const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3697   const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3698   const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3699 
3700   char buf[1024] = {0};
3701   WCHAR bufW[1024] = {0};
3702   LRESULT result;
3703 
3704   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3705      any solitary \r to be converted to \r\n on return. Properly paired
3706      \r\n are not affected. It also shows that the special sequence \r\r\n
3707      gets converted to a single space.
3708    */
3709 
3710 #define TEST_SETTEXT(a, b) \
3711   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3712   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3713   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3714   ok (result == lstrlenA(buf), \
3715 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3716 	result, lstrlenA(buf)); \
3717   result = strcmp(b, buf); \
3718   ok(result == 0, \
3719         "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3720 
3721   TEST_SETTEXT(TestItem1, TestItem1)
3722   TEST_SETTEXT(TestItem2, TestItem2_after)
3723   TEST_SETTEXT(TestItem3, TestItem3_after)
3724   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3725   TEST_SETTEXT(TestItem4, TestItem4_after)
3726   TEST_SETTEXT(TestItem5, TestItem5_after)
3727   TEST_SETTEXT(TestItem6, TestItem6_after)
3728   TEST_SETTEXT(TestItem7, TestItem7_after)
3729 
3730   /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3731   TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3732   TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3733   TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3734   TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3735   DestroyWindow(hwndRichEdit);
3736 #undef TEST_SETTEXT
3737 
3738 #define TEST_SETTEXTW(a, b) \
3739   result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3740   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3741   result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3742   ok (result == lstrlenW(bufW), \
3743 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3744 	result, lstrlenW(bufW)); \
3745   result = lstrcmpW(b, bufW); \
3746   ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3747 
3748   hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3749                                ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3750                                0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3751   ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3752   TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3753   TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3754   TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3755   TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3756   DestroyWindow(hwndRichEdit);
3757 #undef TEST_SETTEXTW
3758 
3759   /* Single-line richedit */
3760   hwndRichEdit = new_richedit_with_style(NULL, 0);
3761   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3762   ok(result == 1, "WM_SETTEXT returned %ld, expected 12\n", result);
3763   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3764   ok(result == 5, "WM_GETTEXT returned %ld, expected 5\n", result);
3765   ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3766   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3767   ok(result == 1, "WM_SETTEXT returned %ld, expected 1\n", result);
3768   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3769   ok(result == 3, "WM_GETTEXT returned %ld, expected 3\n", result);
3770   ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3771   DestroyWindow(hwndRichEdit);
3772 }
3773 
3774 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3775    resent to the callkack. */
3776 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3777                                                 LPBYTE pbBuff,
3778                                                 LONG cb,
3779                                                 LONG *pcb)
3780 {
3781   char** str = (char**)dwCookie;
3782   ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3783   *pcb = 0;
3784   if (cb > 0) {
3785     memcpy(*str, pbBuff, cb);
3786     *str += cb;
3787     *pcb = 1;
3788   }
3789   return 0;
3790 }
3791 
3792 static int count_pars(const char *buf)
3793 {
3794     const char *p = buf;
3795     int count = 0;
3796     while ((p = strstr( p, "\\par" )) != NULL)
3797     {
3798         if (!isalpha( p[4] ))
3799            count++;
3800         p++;
3801     }
3802     return count;
3803 }
3804 
3805 static void test_EM_STREAMOUT(void)
3806 {
3807   HWND hwndRichEdit = new_richedit(NULL);
3808   int r;
3809   EDITSTREAM es;
3810   char buf[1024] = {0};
3811   char * p;
3812   LRESULT result;
3813 
3814   const char * TestItem1 = "TestSomeText";
3815   const char * TestItem2 = "TestSomeText\r";
3816   const char * TestItem3 = "TestSomeText\r\n";
3817 
3818   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3819   p = buf;
3820   es.dwCookie = (DWORD_PTR)&p;
3821   es.dwError = 0;
3822   es.pfnCallback = test_WM_SETTEXT_esCallback;
3823   memset(buf, 0, sizeof(buf));
3824   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3825   r = strlen(buf);
3826   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3827   ok(strcmp(buf, TestItem1) == 0,
3828         "streamed text different, got %s\n", buf);
3829   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3830 
3831   /* RTF mode writes the final end of para \r if it's part of the selection */
3832   p = buf;
3833   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3834   ok (count_pars(buf) == 1, "got %s\n", buf);
3835   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3836   p = buf;
3837   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3838   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3839   ok (count_pars(buf) == 0, "got %s\n", buf);
3840   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3841   p = buf;
3842   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3843   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3844   ok (count_pars(buf) == 1, "got %s\n", buf);
3845   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3846 
3847   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3848   p = buf;
3849   es.dwCookie = (DWORD_PTR)&p;
3850   es.dwError = 0;
3851   es.pfnCallback = test_WM_SETTEXT_esCallback;
3852   memset(buf, 0, sizeof(buf));
3853   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3854   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3855   r = strlen(buf);
3856   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3857   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3858   ok(strcmp(buf, TestItem3) == 0,
3859         "streamed text different from, got %s\n", buf);
3860 
3861   /* And again RTF mode writes the final end of para \r if it's part of the selection */
3862   p = buf;
3863   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3864   ok (count_pars(buf) == 2, "got %s\n", buf);
3865   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3866   p = buf;
3867   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3868   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3869   ok (count_pars(buf) == 1, "got %s\n", buf);
3870   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3871   p = buf;
3872   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3873   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3874   ok (count_pars(buf) == 2, "got %s\n", buf);
3875   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3876 
3877   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3878   p = buf;
3879   es.dwCookie = (DWORD_PTR)&p;
3880   es.dwError = 0;
3881   es.pfnCallback = test_WM_SETTEXT_esCallback;
3882   memset(buf, 0, sizeof(buf));
3883   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3884   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3885   r = strlen(buf);
3886   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3887   ok(strcmp(buf, TestItem3) == 0,
3888         "streamed text different, got %s\n", buf);
3889 
3890   /* Use a callback that sets *pcb to one */
3891   p = buf;
3892   es.dwCookie = (DWORD_PTR)&p;
3893   es.dwError = 0;
3894   es.pfnCallback = test_esCallback_written_1;
3895   memset(buf, 0, sizeof(buf));
3896   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3897   r = strlen(buf);
3898   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3899   ok(strcmp(buf, TestItem3) == 0,
3900         "streamed text different, got %s\n", buf);
3901   ok(result == 0, "got %ld expected 0\n", result);
3902 
3903 
3904   DestroyWindow(hwndRichEdit);
3905 }
3906 
3907 static void test_EM_STREAMOUT_FONTTBL(void)
3908 {
3909   HWND hwndRichEdit = new_richedit(NULL);
3910   EDITSTREAM es;
3911   char buf[1024] = {0};
3912   char * p;
3913   char * fontTbl;
3914   int brackCount;
3915 
3916   const char * TestItem = "TestSomeText";
3917 
3918   /* fills in the richedit control with some text */
3919   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3920 
3921   /* streams out the text in rtf format */
3922   p = buf;
3923   es.dwCookie = (DWORD_PTR)&p;
3924   es.dwError = 0;
3925   es.pfnCallback = test_WM_SETTEXT_esCallback;
3926   memset(buf, 0, sizeof(buf));
3927   SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3928 
3929   /* scans for \fonttbl, error if not found */
3930   fontTbl = strstr(buf, "\\fonttbl");
3931   ok(fontTbl != NULL, "missing \\fonttbl section\n");
3932   if(fontTbl)
3933   {
3934       /* scans for terminating closing bracket */
3935       brackCount = 1;
3936       while(*fontTbl && brackCount)
3937       {
3938           if(*fontTbl == '{')
3939               brackCount++;
3940           else if(*fontTbl == '}')
3941               brackCount--;
3942           fontTbl++;
3943       }
3944     /* checks whether closing bracket is ok */
3945       ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3946       if(!brackCount)
3947       {
3948           /* char before closing fonttbl block should be a closed bracket */
3949           fontTbl -= 2;
3950           ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3951 
3952           /* char after fonttbl block should be a crlf */
3953           fontTbl += 2;
3954           ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3955       }
3956   }
3957   DestroyWindow(hwndRichEdit);
3958 }
3959 
3960 static void test_EM_STREAMOUT_empty_para(void)
3961 {
3962     HWND hwnd = new_richedit(NULL);
3963     char buf[1024], *p = buf;
3964     EDITSTREAM es;
3965 
3966     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
3967 
3968     memset(buf, 0, sizeof(buf));
3969     es.dwCookie    = (DWORD_PTR)&p;
3970     es.dwError     = 0;
3971     es.pfnCallback = test_WM_SETTEXT_esCallback;
3972 
3973     SendMessageA(hwnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3974     ok((p = strstr(buf, "\\pard")) != NULL, "missing \\pard\n");
3975     ok(((p = strstr(p, "\\fs")) && isdigit(p[3])), "missing \\fs\n");
3976 
3977     DestroyWindow(hwnd);
3978 }
3979 
3980 static void test_EM_SETTEXTEX(void)
3981 {
3982   HWND hwndRichEdit, parent;
3983   SCROLLINFO si;
3984   int sel_start, sel_end;
3985   SETTEXTEX setText;
3986   GETTEXTEX getText;
3987   WCHAR TestItem1[] = {'T', 'e', 's', 't',
3988                        'S', 'o', 'm', 'e',
3989                        'T', 'e', 'x', 't', 0};
3990   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3991                           't', 'S', 'o', 'm',
3992                           'e', 'T', 'e', 'x',
3993                           't', 't', 'S', 'o',
3994                           'm', 'e', 'T', 'e',
3995                           'x', 't', 0};
3996   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3997                            '\r','t','S','o','m','e','T','e','x','t',0};
3998   WCHAR TestItem2[] = {'T', 'e', 's', 't',
3999                        'S', 'o', 'm', 'e',
4000                        'T', 'e', 'x', 't',
4001                       '\r', 0};
4002   const char * TestItem2_after = "TestSomeText\r\n";
4003   WCHAR TestItem3[] = {'T', 'e', 's', 't',
4004                        'S', 'o', 'm', 'e',
4005                        'T', 'e', 'x', 't',
4006                       '\r','\n','\r','\n', 0};
4007   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
4008                        'S', 'o', 'm', 'e',
4009                        'T', 'e', 'x', 't',
4010                        '\n','\n', 0};
4011   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
4012                        'S', 'o', 'm', 'e',
4013                        'T', 'e', 'x', 't',
4014                        '\r','\r', 0};
4015   WCHAR TestItem4[] = {'T', 'e', 's', 't',
4016                        'S', 'o', 'm', 'e',
4017                        'T', 'e', 'x', 't',
4018                       '\r','\r','\n','\r',
4019                       '\n', 0};
4020   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
4021                        'S', 'o', 'm', 'e',
4022                        'T', 'e', 'x', 't',
4023                        ' ','\r', 0};
4024 #define MAX_BUF_LEN 1024
4025   WCHAR buf[MAX_BUF_LEN];
4026   char bufACP[MAX_BUF_LEN];
4027   char * p;
4028   int result;
4029   CHARRANGE cr;
4030   EDITSTREAM es;
4031   WNDCLASSA cls;
4032 
4033   /* Test the scroll position with and without a parent window.
4034    *
4035    * For some reason the scroll position is 0 after EM_SETTEXTEX
4036    * with the ST_SELECTION flag only when the control has a parent
4037    * window, even though the selection is at the end. */
4038   cls.style = 0;
4039   cls.lpfnWndProc = DefWindowProcA;
4040   cls.cbClsExtra = 0;
4041   cls.cbWndExtra = 0;
4042   cls.hInstance = GetModuleHandleA(0);
4043   cls.hIcon = 0;
4044   cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
4045   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4046   cls.lpszMenuName = NULL;
4047   cls.lpszClassName = "ParentTestClass";
4048   if(!RegisterClassA(&cls)) assert(0);
4049 
4050   parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4051                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
4052   ok (parent != 0, "Failed to create parent window\n");
4053 
4054   hwndRichEdit = CreateWindowExA(0,
4055                         RICHEDIT_CLASS20A, NULL,
4056                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4057                         0, 0, 200, 60, parent, NULL,
4058                         hmoduleRichEdit, NULL);
4059 
4060   setText.codepage = CP_ACP;
4061   setText.flags = ST_SELECTION;
4062   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4063                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4064   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4065   si.cbSize = sizeof(si);
4066   si.fMask = SIF_ALL;
4067   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4068   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4069   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4070   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4071   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4072 
4073   DestroyWindow(parent);
4074 
4075   /* Test without a parent window */
4076   hwndRichEdit = new_richedit(NULL);
4077   setText.codepage = CP_ACP;
4078   setText.flags = ST_SELECTION;
4079   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4080                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4081   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4082   si.cbSize = sizeof(si);
4083   si.fMask = SIF_ALL;
4084   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4085   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4086   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4087   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4088   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4089 
4090   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4091    * but this time it is because the selection is at the beginning. */
4092   setText.codepage = CP_ACP;
4093   setText.flags = ST_DEFAULT;
4094   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4095                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4096   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4097   si.cbSize = sizeof(si);
4098   si.fMask = SIF_ALL;
4099   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4100   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4101   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4102   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4103   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4104 
4105   setText.codepage = 1200;  /* no constant for unicode */
4106   getText.codepage = 1200;  /* no constant for unicode */
4107   getText.cb = MAX_BUF_LEN;
4108   getText.flags = GT_DEFAULT;
4109   getText.lpDefaultChar = NULL;
4110   getText.lpUsedDefChar = NULL;
4111 
4112   setText.flags = 0;
4113   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4114   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4115   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4116   ok(lstrcmpW(buf, TestItem1) == 0,
4117       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4118 
4119   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4120      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4121    */
4122   setText.codepage = 1200;  /* no constant for unicode */
4123   getText.codepage = 1200;  /* no constant for unicode */
4124   getText.cb = MAX_BUF_LEN;
4125   getText.flags = GT_DEFAULT;
4126   getText.lpDefaultChar = NULL;
4127   getText.lpUsedDefChar = NULL;
4128   setText.flags = 0;
4129   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4130   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4131   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4132   ok(lstrcmpW(buf, TestItem2) == 0,
4133       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4134 
4135   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4136   SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4137   ok(strcmp((const char *)buf, TestItem2_after) == 0,
4138       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4139 
4140   /* Baseline test for just-enough buffer space for string */
4141   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4142   getText.codepage = 1200;  /* no constant for unicode */
4143   getText.flags = GT_DEFAULT;
4144   getText.lpDefaultChar = NULL;
4145   getText.lpUsedDefChar = NULL;
4146   memset(buf, 0, sizeof(buf));
4147   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4148   ok(lstrcmpW(buf, TestItem2) == 0,
4149       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4150 
4151   /* When there is enough space for one character, but not both, of the CRLF
4152      pair at the end of the string, the CR is not copied at all. That is,
4153      the caller must not see CRLF pairs truncated to CR at the end of the
4154      string.
4155    */
4156   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4157   getText.codepage = 1200;  /* no constant for unicode */
4158   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
4159   getText.lpDefaultChar = NULL;
4160   getText.lpUsedDefChar = NULL;
4161   memset(buf, 0, sizeof(buf));
4162   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4163   ok(lstrcmpW(buf, TestItem1) == 0,
4164       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4165 
4166 
4167   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4168   setText.codepage = 1200;  /* no constant for unicode */
4169   getText.codepage = 1200;  /* no constant for unicode */
4170   getText.cb = MAX_BUF_LEN;
4171   getText.flags = GT_DEFAULT;
4172   getText.lpDefaultChar = NULL;
4173   getText.lpUsedDefChar = NULL;
4174   setText.flags = 0;
4175   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4176   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4177   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4178   ok(lstrcmpW(buf, TestItem3_after) == 0,
4179       "EM_SETTEXTEX did not convert properly\n");
4180 
4181   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4182   setText.codepage = 1200;  /* no constant for unicode */
4183   getText.codepage = 1200;  /* no constant for unicode */
4184   getText.cb = MAX_BUF_LEN;
4185   getText.flags = GT_DEFAULT;
4186   getText.lpDefaultChar = NULL;
4187   getText.lpUsedDefChar = NULL;
4188   setText.flags = 0;
4189   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4190   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4191   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4192   ok(lstrcmpW(buf, TestItem3_after) == 0,
4193       "EM_SETTEXTEX did not convert properly\n");
4194 
4195   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4196   setText.codepage = 1200;  /* no constant for unicode */
4197   getText.codepage = 1200;  /* no constant for unicode */
4198   getText.cb = MAX_BUF_LEN;
4199   getText.flags = GT_DEFAULT;
4200   getText.lpDefaultChar = NULL;
4201   getText.lpUsedDefChar = NULL;
4202   setText.flags = 0;
4203   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4204   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4205   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4206   ok(lstrcmpW(buf, TestItem4_after) == 0,
4207       "EM_SETTEXTEX did not convert properly\n");
4208 
4209   /* !ST_SELECTION && Unicode && !\rtf */
4210   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4211   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4212 
4213   ok (result == 1,
4214       "EM_SETTEXTEX returned %d, instead of 1\n",result);
4215   ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4216 
4217   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4218   setText.flags = 0;
4219   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4220   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4221   /* select some text */
4222   cr.cpMax = 1;
4223   cr.cpMin = 3;
4224   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4225   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4226   setText.flags = ST_SELECTION;
4227   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4228   ok(result == 0,
4229       "EM_SETTEXTEX with NULL lParam to replace selection"
4230       " with no text should return 0. Got %i\n",
4231       result);
4232 
4233   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4234   setText.flags = 0;
4235   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4236   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4237   /* select some text */
4238   cr.cpMax = 1;
4239   cr.cpMin = 3;
4240   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4241   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4242   setText.flags = ST_SELECTION;
4243   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4244   /* get text */
4245   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4246   ok(result == lstrlenW(TestItem1),
4247       "EM_SETTEXTEX with NULL lParam to replace selection"
4248       " with no text should return 0. Got %i\n",
4249       result);
4250   ok(lstrlenW(buf) == 22,
4251       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4252       lstrlenW(buf) );
4253 
4254   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4255   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4256   p = (char *)buf;
4257   es.dwCookie = (DWORD_PTR)&p;
4258   es.dwError = 0;
4259   es.pfnCallback = test_WM_SETTEXT_esCallback;
4260   memset(buf, 0, sizeof(buf));
4261   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4262               (WPARAM)(SF_RTF), (LPARAM)&es);
4263   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4264 
4265   /* !ST_SELECTION && !Unicode && \rtf */
4266   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4267   getText.codepage = 1200;  /* no constant for unicode */
4268   getText.cb = MAX_BUF_LEN;
4269   getText.flags = GT_DEFAULT;
4270   getText.lpDefaultChar = NULL;
4271   getText.lpUsedDefChar = NULL;
4272 
4273   setText.flags = 0;
4274   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4275   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4276   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4277   ok(lstrcmpW(buf, TestItem1) == 0,
4278       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4279 
4280   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4281    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4282   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4283   getText.codepage = CP_ACP;
4284   getText.cb = MAX_BUF_LEN;
4285   getText.flags = GT_DEFAULT;
4286   getText.lpDefaultChar = NULL;
4287   getText.lpUsedDefChar = NULL;
4288 
4289   setText.flags = ST_SELECTION;
4290   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4291   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4292   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4293   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4294   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4295 
4296   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4297   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4298   p = (char *)buf;
4299   es.dwCookie = (DWORD_PTR)&p;
4300   es.dwError = 0;
4301   es.pfnCallback = test_WM_SETTEXT_esCallback;
4302   memset(buf, 0, sizeof(buf));
4303   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4304               (WPARAM)(SF_RTF), (LPARAM)&es);
4305   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4306 
4307   /* select some text */
4308   cr.cpMax = 1;
4309   cr.cpMin = 3;
4310   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4311 
4312   /* ST_SELECTION && !Unicode && \rtf */
4313   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4314   getText.codepage = 1200;  /* no constant for unicode */
4315   getText.cb = MAX_BUF_LEN;
4316   getText.flags = GT_DEFAULT;
4317   getText.lpDefaultChar = NULL;
4318   getText.lpUsedDefChar = NULL;
4319 
4320   setText.flags = ST_SELECTION;
4321   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4322   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4323   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4324 
4325   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4326   setText.codepage = 1200;  /* no constant for unicode */
4327   getText.codepage = CP_ACP;
4328   getText.cb = MAX_BUF_LEN;
4329 
4330   setText.flags = 0;
4331   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4332   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4333 
4334   /* select some text */
4335   cr.cpMax = 1;
4336   cr.cpMin = 3;
4337   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4338 
4339   /* ST_SELECTION && !Unicode && !\rtf */
4340   setText.codepage = CP_ACP;
4341   getText.codepage = 1200;  /* no constant for unicode */
4342   getText.cb = MAX_BUF_LEN;
4343   getText.flags = GT_DEFAULT;
4344   getText.lpDefaultChar = NULL;
4345   getText.lpUsedDefChar = NULL;
4346 
4347   setText.flags = ST_SELECTION;
4348   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4349   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4350   ok(lstrcmpW(buf, TestItem1alt) == 0,
4351       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4352       " using ST_SELECTION and non-Unicode\n");
4353 
4354   /* Test setting text using rich text format */
4355   setText.flags = 0;
4356   setText.codepage = CP_ACP;
4357   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4358   getText.codepage = CP_ACP;
4359   getText.cb = MAX_BUF_LEN;
4360   getText.flags = GT_DEFAULT;
4361   getText.lpDefaultChar = NULL;
4362   getText.lpUsedDefChar = NULL;
4363   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4364   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4365 
4366   setText.flags = 0;
4367   setText.codepage = CP_ACP;
4368   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4369   getText.codepage = CP_ACP;
4370   getText.cb = MAX_BUF_LEN;
4371   getText.flags = GT_DEFAULT;
4372   getText.lpDefaultChar = NULL;
4373   getText.lpUsedDefChar = NULL;
4374   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4375   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4376 
4377   /* test for utf8 text with BOM */
4378   setText.flags = 0;
4379   setText.codepage = CP_ACP;
4380   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4381   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4382   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4383   result = strcmp(bufACP, "TestUTF8WithBOM");
4384   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4385 
4386   setText.flags = 0;
4387   setText.codepage = CP_UTF8;
4388   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4389   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4390   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4391   result = strcmp(bufACP, "TestUTF8WithBOM");
4392   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4393 
4394   /* Test multibyte character */
4395   if (!is_lang_japanese)
4396     skip("Skip multibyte character tests on non-Japanese platform\n");
4397   else
4398   {
4399     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4400     setText.flags = ST_SELECTION;
4401     setText.codepage = CP_ACP;
4402     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4403     todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4404     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4405     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4406     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4407        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4408 
4409     setText.flags = ST_DEFAULT;
4410     setText.codepage = CP_ACP;
4411     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4412     ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4413     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4414     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4415     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4416        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4417 
4418     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4419     setText.flags = ST_SELECTION;
4420     setText.codepage = CP_ACP;
4421     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4422     todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4423     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4424     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4425     todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4426                  "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4427   }
4428 
4429   DestroyWindow(hwndRichEdit);
4430 
4431   /* Single-line richedit */
4432   hwndRichEdit = new_richedit_with_style(NULL, 0);
4433   setText.flags = ST_DEFAULT;
4434   setText.codepage = CP_ACP;
4435   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"line1\r\nline2");
4436   ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4437   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4438   ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4439   ok(!strcmp(bufACP, "line1"), "EM_SETTEXTEX: Test single-line text: Result: %s\n", bufACP);
4440   DestroyWindow(hwndRichEdit);
4441 }
4442 
4443 static void test_EM_LIMITTEXT(void)
4444 {
4445   int ret;
4446 
4447   HWND hwndRichEdit = new_richedit(NULL);
4448 
4449   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4450    * about setting the length to -1 for multiline edit controls doesn't happen.
4451    */
4452 
4453   /* Don't check default gettextlimit case. That's done in other tests */
4454 
4455   /* Set textlimit to 100 */
4456   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4457   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4458   ok (ret == 100,
4459       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4460 
4461   /* Set textlimit to 0 */
4462   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4463   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4464   ok (ret == 65536,
4465       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4466 
4467   /* Set textlimit to -1 */
4468   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4469   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4470   ok (ret == -1,
4471       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4472 
4473   /* Set textlimit to -2 */
4474   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4475   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4476   ok (ret == -2,
4477       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4478 
4479   DestroyWindow (hwndRichEdit);
4480 }
4481 
4482 
4483 static void test_EM_EXLIMITTEXT(void)
4484 {
4485   int i, selBegin, selEnd, len1, len2;
4486   int result;
4487   char text[1024 + 1];
4488   char buffer[1024 + 1];
4489   int textlimit = 0; /* multiple of 100 */
4490   HWND hwndRichEdit = new_richedit(NULL);
4491 
4492   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4493   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4494 
4495   textlimit = 256000;
4496   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4497   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4498   /* set higher */
4499   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4500 
4501   textlimit = 1000;
4502   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4503   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4504   /* set lower */
4505   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4506 
4507   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4508   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4509   /* default for WParam = 0 */
4510   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4511 
4512   textlimit = sizeof(text)-1;
4513   memset(text, 'W', textlimit);
4514   text[sizeof(text)-1] = 0;
4515   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4516   /* maxed out text */
4517   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4518 
4519   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4520   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4521   len1 = selEnd - selBegin;
4522 
4523   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4524   SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4525   SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4526   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4527   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4528   len2 = selEnd - selBegin;
4529 
4530   ok(len1 != len2,
4531     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4532     len1,len2,i);
4533 
4534   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4535   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4536   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4537   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4538   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4539   len1 = selEnd - selBegin;
4540 
4541   ok(len1 != len2,
4542     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4543     len1,len2,i);
4544 
4545   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4546   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4547   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
4548   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4549   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4550   len2 = selEnd - selBegin;
4551 
4552   ok(len1 == len2,
4553     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4554     len1,len2,i);
4555 
4556   /* set text up to the limit, select all the text, then add a char */
4557   textlimit = 5;
4558   memset(text, 'W', textlimit);
4559   text[textlimit] = 0;
4560   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4561   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4562   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4563   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4564   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4565   result = strcmp(buffer, "A");
4566   ok(0 == result, "got string = \"%s\"\n", buffer);
4567 
4568   /* WM_SETTEXT not limited */
4569   textlimit = 10;
4570   memset(text, 'W', textlimit);
4571   text[textlimit] = 0;
4572   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4573   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4574   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4575   i = strlen(buffer);
4576   ok(10 == i, "expected 10 chars\n");
4577   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4578   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4579 
4580   /* try inserting more text at end */
4581   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4582   ok(0 == i, "WM_CHAR wasn't processed\n");
4583   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4584   i = strlen(buffer);
4585   ok(10 == i, "expected 10 chars, got %i\n", i);
4586   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4587   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4588 
4589   /* try inserting text at beginning */
4590   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4591   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4592   ok(0 == i, "WM_CHAR wasn't processed\n");
4593   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4594   i = strlen(buffer);
4595   ok(10 == i, "expected 10 chars, got %i\n", i);
4596   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4597   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4598 
4599   /* WM_CHAR is limited */
4600   textlimit = 1;
4601   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4602   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4603   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4604   ok(0 == i, "WM_CHAR wasn't processed\n");
4605   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4606   ok(0 == i, "WM_CHAR wasn't processed\n");
4607   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4608   i = strlen(buffer);
4609   ok(1 == i, "expected 1 chars, got %i instead\n", i);
4610 
4611   DestroyWindow(hwndRichEdit);
4612 }
4613 
4614 static void test_EM_GETLIMITTEXT(void)
4615 {
4616   int i;
4617   HWND hwndRichEdit = new_richedit(NULL);
4618 
4619   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4620   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4621 
4622   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4623   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4624   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4625 
4626   DestroyWindow(hwndRichEdit);
4627 }
4628 
4629 static void test_WM_SETFONT(void)
4630 {
4631   /* There is no invalid input or error conditions for this function.
4632    * NULL wParam and lParam just fall back to their default values
4633    * It should be noted that even if you use a gibberish name for your fonts
4634    * here, it will still work because the name is stored. They will display as
4635    * System, but will report their name to be whatever they were created as */
4636 
4637   HWND hwndRichEdit = new_richedit(NULL);
4638   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4639     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4640     FF_DONTCARE, "Marlett");
4641   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4642     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4643     FF_DONTCARE, "MS Sans Serif");
4644   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4645     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4646     FF_DONTCARE, "Courier");
4647   LOGFONTA sentLogFont;
4648   CHARFORMAT2A returnedCF2A;
4649 
4650   returnedCF2A.cbSize = sizeof(returnedCF2A);
4651 
4652   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4653   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4654   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4655 
4656   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4657   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4658     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4659     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4660 
4661   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4662   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4663   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4664   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4665     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4666     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4667 
4668   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4669   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4670   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4671   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4672     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4673     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4674 
4675   /* This last test is special since we send in NULL. We clear the variables
4676    * and just compare to "System" instead of the sent in font name. */
4677   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4678   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4679   returnedCF2A.cbSize = sizeof(returnedCF2A);
4680 
4681   SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4682   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4683   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4684   ok (!strcmp("System",returnedCF2A.szFaceName),
4685     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4686 
4687   DestroyWindow(hwndRichEdit);
4688 }
4689 
4690 
4691 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4692                                          LPBYTE pbBuff,
4693                                          LONG cb,
4694                                          LONG *pcb)
4695 {
4696   const char** str = (const char**)dwCookie;
4697   int size = strlen(*str);
4698   if(size > 3)  /* let's make it piecemeal for fun */
4699     size = 3;
4700   *pcb = cb;
4701   if (*pcb > size) {
4702     *pcb = size;
4703   }
4704   if (*pcb > 0) {
4705     memcpy(pbBuff, *str, *pcb);
4706     *str += *pcb;
4707   }
4708   return 0;
4709 }
4710 
4711 static void test_EM_GETMODIFY(void)
4712 {
4713   HWND hwndRichEdit = new_richedit(NULL);
4714   LRESULT result;
4715   SETTEXTEX setText;
4716   WCHAR TestItem1[] = {'T', 'e', 's', 't',
4717                        'S', 'o', 'm', 'e',
4718                        'T', 'e', 'x', 't', 0};
4719   WCHAR TestItem2[] = {'T', 'e', 's', 't',
4720                        'S', 'o', 'm', 'e',
4721                        'O', 't', 'h', 'e', 'r',
4722                        'T', 'e', 'x', 't', 0};
4723   const char* streamText = "hello world";
4724   CHARFORMAT2A cf2;
4725   PARAFORMAT2 pf2;
4726   EDITSTREAM es;
4727 
4728   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4729     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4730     FF_DONTCARE, "Courier");
4731 
4732   setText.codepage = 1200;  /* no constant for unicode */
4733   setText.flags = ST_KEEPUNDO;
4734 
4735 
4736   /* modify flag shouldn't be set when richedit is first created */
4737   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4738   ok (result == 0,
4739       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4740 
4741   /* setting modify flag should actually set it */
4742   SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4743   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4744   ok (result != 0,
4745       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4746 
4747   /* clearing modify flag should actually clear it */
4748   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4749   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4750   ok (result == 0,
4751       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4752 
4753   /* setting font doesn't change modify flag */
4754   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4755   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4756   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4757   ok (result == 0,
4758       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4759 
4760   /* setting text should set modify flag */
4761   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4762   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4763   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4764   ok (result != 0,
4765       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4766 
4767   /* undo previous text doesn't reset modify flag */
4768   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4769   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4770   ok (result != 0,
4771       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4772 
4773   /* set text with no flag to keep undo stack should not set modify flag */
4774   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4775   setText.flags = 0;
4776   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4777   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4778   ok (result == 0,
4779       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4780 
4781   /* WM_SETTEXT doesn't modify */
4782   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4783   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4784   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4785   ok (result == 0,
4786       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4787 
4788   /* clear the text */
4789   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4790   SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4791   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4792   ok (result == 0,
4793       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4794 
4795   /* replace text */
4796   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4797   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4798   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4799   SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4800   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4801   ok (result != 0,
4802       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4803 
4804   /* copy/paste text 1 */
4805   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4806   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4807   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4808   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4809   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4810   ok (result != 0,
4811       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4812 
4813   /* copy/paste text 2 */
4814   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4815   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4816   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4817   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4818   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4819   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4820   ok (result != 0,
4821       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4822 
4823   /* press char */
4824   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4825   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4826   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4827   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4828   ok (result != 0,
4829       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4830 
4831   /* press del */
4832   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4833   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4834   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4835   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4836   ok (result != 0,
4837       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4838 
4839   /* set char format */
4840   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4841   cf2.cbSize = sizeof(CHARFORMAT2A);
4842   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4843   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4844   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4845   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4846   result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4847   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4848   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4849   ok (result != 0,
4850       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4851 
4852   /* set para format */
4853   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4854   pf2.cbSize = sizeof(PARAFORMAT2);
4855   SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4856   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4857   pf2.wAlignment = PFA_RIGHT;
4858   SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4859   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4860   ok (result == 0,
4861       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4862 
4863   /* EM_STREAM */
4864   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4865   es.dwCookie = (DWORD_PTR)&streamText;
4866   es.dwError = 0;
4867   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4868   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4869   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4870   ok (result != 0,
4871       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4872 
4873   DestroyWindow(hwndRichEdit);
4874 }
4875 
4876 struct exsetsel_s {
4877   LONG min;
4878   LONG max;
4879   LRESULT expected_retval;
4880   int expected_getsel_start;
4881   int expected_getsel_end;
4882   BOOL todo;
4883 };
4884 
4885 static const struct exsetsel_s exsetsel_tests[] = {
4886   /* sanity tests */
4887   {5, 10, 10, 5, 10 },
4888   {15, 17, 17, 15, 17 },
4889   /* test cpMax > strlen() */
4890   {0, 100, 18, 0, 18 },
4891   /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4892   {-1, 1, 17, 17, 17 },
4893   /* test cpMin == cpMax */
4894   {5, 5, 5, 5, 5 },
4895   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4896   {-1, 0, 5, 5, 5 },
4897   {-1, 17, 5, 5, 5 },
4898   {-1, 18, 5, 5, 5 },
4899   /* test cpMin < 0 && cpMax < 0 */
4900   {-1, -1, 17, 17, 17 },
4901   {-4, -5, 17, 17, 17 },
4902   /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4903   {0, -1, 18, 0, 18 },
4904   {17, -5, 18, 17, 18 },
4905   {18, -3, 17, 17, 17 },
4906   /* test if cpMin > cpMax */
4907   {15, 19, 18, 15, 18 },
4908   {19, 15, 18, 15, 18 },
4909   /* cpMin == strlen() && cpMax > cpMin */
4910   {17, 18, 18, 17, 18 },
4911   {17, 50, 18, 17, 18 },
4912 };
4913 
4914 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4915     CHARRANGE cr;
4916     LRESULT result;
4917     int start, end;
4918 
4919     cr.cpMin = setsel->min;
4920     cr.cpMax = setsel->max;
4921     result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4922 
4923     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4924 
4925     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4926 
4927     todo_wine_if (setsel->todo)
4928         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4929             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4930 }
4931 
4932 static void test_EM_EXSETSEL(void)
4933 {
4934     HWND hwndRichEdit = new_richedit(NULL);
4935     int i;
4936     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4937 
4938     /* sending some text to the window */
4939     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4940     /*                                                 01234567890123456*/
4941     /*                                                          10      */
4942 
4943     for (i = 0; i < num_tests; i++) {
4944         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4945     }
4946 
4947     if (!is_lang_japanese)
4948         skip("Skip multibyte character tests on non-Japanese platform\n");
4949     else
4950     {
4951         CHARRANGE cr;
4952         char bufA[MAX_BUF_LEN] = {0};
4953         LRESULT result;
4954 
4955         /* Test with multibyte character */
4956         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4957         /*                                                 012345     6  78901 */
4958         cr.cpMin = 4, cr.cpMax = 8;
4959         result =  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4960         ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4961         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4962         ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4963         SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4964         ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4965         ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4966     }
4967 
4968     DestroyWindow(hwndRichEdit);
4969 }
4970 
4971 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4972     LRESULT result;
4973     int start, end;
4974 
4975     result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4976 
4977     ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4978 
4979     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4980 
4981     todo_wine_if (setsel->todo)
4982         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4983             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4984 }
4985 
4986 static void test_EM_SETSEL(void)
4987 {
4988     char buffA[32] = {0};
4989     HWND hwndRichEdit = new_richedit(NULL);
4990     int i;
4991     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4992 
4993     /* sending some text to the window */
4994     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4995     /*                                                 01234567890123456*/
4996     /*                                                          10      */
4997 
4998     for (i = 0; i < num_tests; i++) {
4999         check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
5000     }
5001 
5002     SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
5003     buffA[0] = 123;
5004     SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5005     ok(buffA[0] == 0, "selection text %s\n", buffA);
5006 
5007     if (!is_lang_japanese)
5008         skip("Skip multibyte character tests on non-Japanese platform\n");
5009     else
5010     {
5011         int sel_start, sel_end;
5012         LRESULT result;
5013 
5014         /* Test with multibyte character */
5015         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5016         /*                                                 012345     6  78901 */
5017         result =  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
5018         ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
5019         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
5020         ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5021         result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5022         ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
5023         ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
5024     }
5025 
5026     DestroyWindow(hwndRichEdit);
5027 }
5028 
5029 static void test_EM_REPLACESEL(int redraw)
5030 {
5031     HWND hwndRichEdit = new_richedit(NULL);
5032     char buffer[1024] = {0};
5033     int r;
5034     GETTEXTEX getText;
5035     CHARRANGE cr;
5036     CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
5037     CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
5038 
5039     /* sending some text to the window */
5040     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5041     /*                                                 01234567890123456*/
5042     /*                                                          10      */
5043 
5044     /* FIXME add more tests */
5045     SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
5046     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
5047     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
5048     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5049     r = strcmp(buffer, "testing");
5050     ok(0 == r, "expected %d, got %d\n", 0, r);
5051 
5052     DestroyWindow(hwndRichEdit);
5053 
5054     hwndRichEdit = new_richedit(NULL);
5055 
5056     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5057     SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5058 
5059     /* Test behavior with carriage returns and newlines */
5060     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5061     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5062     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5063     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5064     r = strcmp(buffer, "RichEdit1");
5065     ok(0 == r, "expected %d, got %d\n", 0, r);
5066     getText.cb = 1024;
5067     getText.codepage = CP_ACP;
5068     getText.flags = GT_DEFAULT;
5069     getText.lpDefaultChar = NULL;
5070     getText.lpUsedDefChar = NULL;
5071     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5072     ok(strcmp(buffer, "RichEdit1") == 0,
5073       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5074 
5075     /* Test number of lines reported after EM_REPLACESEL */
5076     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5077     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5078 
5079     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5080     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5081     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5082     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5083     r = strcmp(buffer, "RichEdit1\r\n");
5084     ok(0 == r, "expected %d, got %d\n", 0, r);
5085     getText.cb = 1024;
5086     getText.codepage = CP_ACP;
5087     getText.flags = GT_DEFAULT;
5088     getText.lpDefaultChar = NULL;
5089     getText.lpUsedDefChar = NULL;
5090     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5091     ok(strcmp(buffer, "RichEdit1\r") == 0,
5092       "EM_GETTEXTEX returned incorrect string\n");
5093 
5094     /* Test number of lines reported after EM_REPLACESEL */
5095     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5096     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5097 
5098     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5099     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5100     ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5101 
5102     /* Test number of lines reported after EM_REPLACESEL */
5103     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5104     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5105 
5106     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5107     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5108     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5109     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5110 
5111     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5112     r = strcmp(buffer, "RichEdit1\r\n");
5113     ok(0 == r, "expected %d, got %d\n", 0, r);
5114     getText.cb = 1024;
5115     getText.codepage = CP_ACP;
5116     getText.flags = GT_DEFAULT;
5117     getText.lpDefaultChar = NULL;
5118     getText.lpUsedDefChar = NULL;
5119     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5120     ok(strcmp(buffer, "RichEdit1\r") == 0,
5121       "EM_GETTEXTEX returned incorrect string\n");
5122 
5123     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5124     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5125     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5126     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5127 
5128     /* The following tests show that richedit should handle the special \r\r\n
5129        sequence by turning it into a single space on insertion. However,
5130        EM_REPLACESEL on WinXP returns the number of characters in the original
5131        string.
5132      */
5133 
5134     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5135     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5136     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5137     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5138     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5139     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5140     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5141 
5142     /* Test the actual string */
5143     getText.cb = 1024;
5144     getText.codepage = CP_ACP;
5145     getText.flags = GT_DEFAULT;
5146     getText.lpDefaultChar = NULL;
5147     getText.lpUsedDefChar = NULL;
5148     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5149     ok(strcmp(buffer, "\r\r") == 0,
5150       "EM_GETTEXTEX returned incorrect string\n");
5151 
5152     /* Test number of lines reported after EM_REPLACESEL */
5153     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5154     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5155 
5156     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5157     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5158     ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5159     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5160     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5161     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
5162     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
5163 
5164     /* Test the actual string */
5165     getText.cb = 1024;
5166     getText.codepage = CP_ACP;
5167     getText.flags = GT_DEFAULT;
5168     getText.lpDefaultChar = NULL;
5169     getText.lpUsedDefChar = NULL;
5170     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5171     ok(strcmp(buffer, " ") == 0,
5172       "EM_GETTEXTEX returned incorrect string\n");
5173 
5174     /* Test number of lines reported after EM_REPLACESEL */
5175     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5176     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5177 
5178     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5179     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5180     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5181     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5182     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5183     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5184     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5185 
5186     /* Test the actual string */
5187     getText.cb = 1024;
5188     getText.codepage = CP_ACP;
5189     getText.flags = GT_DEFAULT;
5190     getText.lpDefaultChar = NULL;
5191     getText.lpUsedDefChar = NULL;
5192     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5193     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5194       "EM_GETTEXTEX returned incorrect string\n");
5195 
5196     /* Test number of lines reported after EM_REPLACESEL */
5197     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5198     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5199 
5200     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5201     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5202     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5203     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5204     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5205     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5206     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5207 
5208     /* Test the actual string */
5209     getText.cb = 1024;
5210     getText.codepage = CP_ACP;
5211     getText.flags = GT_DEFAULT;
5212     getText.lpDefaultChar = NULL;
5213     getText.lpUsedDefChar = NULL;
5214     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5215     ok(strcmp(buffer, " \r") == 0,
5216       "EM_GETTEXTEX returned incorrect string\n");
5217 
5218     /* Test number of lines reported after EM_REPLACESEL */
5219     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5220     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5221 
5222     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5223     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5224     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5225     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5226     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5227     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
5228     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
5229 
5230     /* Test the actual string */
5231     getText.cb = 1024;
5232     getText.codepage = CP_ACP;
5233     getText.flags = GT_DEFAULT;
5234     getText.lpDefaultChar = NULL;
5235     getText.lpUsedDefChar = NULL;
5236     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5237     ok(strcmp(buffer, " \r\r") == 0,
5238       "EM_GETTEXTEX returned incorrect string\n");
5239 
5240     /* Test number of lines reported after EM_REPLACESEL */
5241     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5242     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5243 
5244     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5245     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5246     ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5247     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5248     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5249     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
5250     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
5251 
5252     /* Test the actual string */
5253     getText.cb = 1024;
5254     getText.codepage = CP_ACP;
5255     getText.flags = GT_DEFAULT;
5256     getText.lpDefaultChar = NULL;
5257     getText.lpUsedDefChar = NULL;
5258     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5259     ok(strcmp(buffer, "\rX\r\r\r") == 0,
5260       "EM_GETTEXTEX returned incorrect string\n");
5261 
5262     /* Test number of lines reported after EM_REPLACESEL */
5263     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5264     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5265 
5266     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5267     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5268     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5269     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5270     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5271     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5272     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5273 
5274     /* Test the actual string */
5275     getText.cb = 1024;
5276     getText.codepage = CP_ACP;
5277     getText.flags = GT_DEFAULT;
5278     getText.lpDefaultChar = NULL;
5279     getText.lpUsedDefChar = NULL;
5280     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5281     ok(strcmp(buffer, "\r\r") == 0,
5282       "EM_GETTEXTEX returned incorrect string\n");
5283 
5284     /* Test number of lines reported after EM_REPLACESEL */
5285     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5286     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5287 
5288     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5289     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5290     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5291     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5292     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5293     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5294     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5295 
5296     /* Test the actual string */
5297     getText.cb = 1024;
5298     getText.codepage = CP_ACP;
5299     getText.flags = GT_DEFAULT;
5300     getText.lpDefaultChar = NULL;
5301     getText.lpUsedDefChar = NULL;
5302     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5303     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5304       "EM_GETTEXTEX returned incorrect string\n");
5305 
5306     /* Test number of lines reported after EM_REPLACESEL */
5307     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5308     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5309 
5310     /* Test with  multibyte character */
5311     if (!is_lang_japanese)
5312         skip("Skip multibyte character tests on non-Japanese platform\n");
5313     else
5314     {
5315         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5316         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5317         todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5318         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5319         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5320         ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5321         ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5322         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5323         ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5324         ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5325 
5326         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5327         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5328         todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5329         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5330         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5331         todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5332         todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5333         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5334         todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5335         todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5336     }
5337 
5338     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5339     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5340     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5341     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5342     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5343     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5344     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5345     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5346     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5347 
5348     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5349     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5350     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5351     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5352     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5353     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5354     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5355     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5356     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5357 
5358     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5359     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5360     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5361     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5362     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5363     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5364     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5365     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5366     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5367     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5368 
5369     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5370     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5371     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5372     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5373     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5374     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5375     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5376     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5377     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5378     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5379 
5380     if (!redraw)
5381         /* This is needed to avoid interfering with keybd_event calls
5382          * on other tests that simulate keyboard events. */
5383         SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5384 
5385     DestroyWindow(hwndRichEdit);
5386 
5387     /* Single-line richedit */
5388     hwndRichEdit = new_richedit_with_style(NULL, 0);
5389     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"line1\r\nline2");
5390     ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5391     r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5392     ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5393     ok(!strcmp(buffer, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buffer);
5394     DestroyWindow(hwndRichEdit);
5395 }
5396 
5397 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5398  * to test the state of the modifiers (Ctrl/Alt/Shift).
5399  *
5400  * Therefore Ctrl-<key> keystrokes need to be simulated with
5401  * keybd_event or by using SetKeyboardState to set the modifiers
5402  * and SendMessage to simulate the keystrokes.
5403  */
5404 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5405 {
5406     LRESULT result;
5407     hold_key(VK_CONTROL);
5408     result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5409     release_key(VK_CONTROL);
5410     return result;
5411 }
5412 
5413 static void test_WM_PASTE(void)
5414 {
5415     int result;
5416     char buffer[1024] = {0};
5417     const char* text1 = "testing paste\r";
5418     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5419     const char* text1_after = "testing paste\r\n";
5420     const char* text2 = "testing paste\r\rtesting paste";
5421     const char* text2_after = "testing paste\r\n\r\ntesting paste";
5422     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5423     HWND hwndRichEdit = new_richedit(NULL);
5424 
5425     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5426     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5427 
5428     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5429     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5430     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5431     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5432     /* Pasted text should be visible at this step */
5433     result = strcmp(text1_step1, buffer);
5434     ok(result == 0,
5435         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5436 
5437     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5438     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5439     /* Text should be the same as before (except for \r -> \r\n conversion) */
5440     result = strcmp(text1_after, buffer);
5441     ok(result == 0,
5442         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5443 
5444     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5445     SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5446     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5447     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5448     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5449     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5450     /* Pasted text should be visible at this step */
5451     result = strcmp(text3, buffer);
5452     ok(result == 0,
5453         "test paste: strcmp = %i\n", result);
5454     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5455     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5456     /* Text should be the same as before (except for \r -> \r\n conversion) */
5457     result = strcmp(text2_after, buffer);
5458     ok(result == 0,
5459         "test paste: strcmp = %i\n", result);
5460     send_ctrl_key(hwndRichEdit, 'Y');   /* Redo */
5461     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5462     /* Text should revert to post-paste state */
5463     result = strcmp(buffer,text3);
5464     ok(result == 0,
5465         "test paste: strcmp = %i\n", result);
5466 
5467     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5468     /* Send WM_CHAR to simulate Ctrl-V */
5469     SendMessageA(hwndRichEdit, WM_CHAR, 22,
5470                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5471     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5472     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5473     result = strcmp(buffer,"");
5474     ok(result == 0,
5475         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5476 
5477     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5478      * with SetKeyboard state. */
5479 
5480     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5481     /* Simulates paste (Ctrl-V) */
5482     hold_key(VK_CONTROL);
5483     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5484                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5485     release_key(VK_CONTROL);
5486     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5487     result = strcmp(buffer,"paste");
5488     ok(result == 0,
5489         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5490 
5491     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5492     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5493     /* Simulates copy (Ctrl-C) */
5494     hold_key(VK_CONTROL);
5495     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5496                 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5497     release_key(VK_CONTROL);
5498     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5499     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5500     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5501     result = strcmp(buffer,"testing");
5502     ok(result == 0,
5503         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5504 
5505     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5506     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5507     /* Simulates select all (Ctrl-A) */
5508     hold_key(VK_CONTROL);
5509     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5510                 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5511     /* Simulates select cut (Ctrl-X) */
5512     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5513                 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5514     release_key(VK_CONTROL);
5515     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5516     result = strcmp(buffer,"");
5517     ok(result == 0,
5518         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5519     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5520     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5521     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5522     result = strcmp(buffer,"cut\r\n");
5523     ok(result == 0,
5524         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5525     /* Simulates undo (Ctrl-Z) */
5526     hold_key(VK_CONTROL);
5527     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5528                 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5529     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5530     result = strcmp(buffer,"");
5531     ok(result == 0,
5532         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5533     /* Simulates redo (Ctrl-Y) */
5534     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5535                 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5536     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5537     result = strcmp(buffer,"cut\r\n");
5538     ok(result == 0,
5539         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5540     release_key(VK_CONTROL);
5541 
5542     /* Copy multiline text to clipboard for future use */
5543     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
5544     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5545     SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5546     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
5547 
5548     /* Paste into read-only control */
5549     result = SendMessageA(hwndRichEdit, EM_SETREADONLY, TRUE, 0);
5550     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5551     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5552     result = strcmp(buffer, text3);
5553     ok(result == 0,
5554         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5555 
5556     /* Cut from read-only control */
5557     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5558     SendMessageA(hwndRichEdit, WM_CUT, 0, 0);
5559     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5560     result = strcmp(buffer, text3);
5561     ok(result == 0,
5562         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5563 
5564     /* FIXME: Wine doesn't flush Ole clipboard when window is destroyed so do it manually */
5565     OleFlushClipboard();
5566     DestroyWindow(hwndRichEdit);
5567 
5568     /* Paste multi-line text into single-line control */
5569     hwndRichEdit = new_richedit_with_style(NULL, 0);
5570     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5571     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5572     result = strcmp(buffer, "testing paste");
5573     ok(result == 0,
5574         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5575     DestroyWindow(hwndRichEdit);
5576 }
5577 
5578 static void test_EM_FORMATRANGE(void)
5579 {
5580   int r, i, tpp_x, tpp_y;
5581   HDC hdc;
5582   HWND hwndRichEdit = new_richedit(NULL);
5583   FORMATRANGE fr;
5584   BOOL skip_non_english;
5585   static const struct {
5586     const char *string; /* The string */
5587     int first;          /* First 'pagebreak', 0 for don't care */
5588     int second;         /* Second 'pagebreak', 0 for don't care */
5589   } fmtstrings[] = {
5590     {"WINE wine", 0, 0},
5591     {"WINE wineWine", 0, 0},
5592     {"WINE\r\nwine\r\nwine", 5, 10},
5593     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5594     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5595   };
5596 
5597   skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5598   if (skip_non_english)
5599     skip("Skipping some tests on non-English platform\n");
5600 
5601   hdc = GetDC(hwndRichEdit);
5602   ok(hdc != NULL, "Could not get HDC\n");
5603 
5604   /* Calculate the twips per pixel */
5605   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5606   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5607 
5608   /* Test the simple case where all the text fits in the page rect. */
5609   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5610   fr.hdc = fr.hdcTarget = hdc;
5611   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5612   fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5613   fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5614   fr.chrg.cpMin = 0;
5615   fr.chrg.cpMax = -1;
5616   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5617   todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5618 
5619   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5620   fr.rc.bottom = fr.rcPage.bottom;
5621   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5622   todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5623 
5624   SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5625 
5626   for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5627   {
5628     GETTEXTLENGTHEX gtl;
5629     SIZE stringsize;
5630     int len;
5631 
5632     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5633 
5634     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5635     gtl.codepage = CP_ACP;
5636     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5637 
5638     /* Get some size information for the string */
5639     GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5640 
5641     /* Define the box to be half the width needed and a bit larger than the height.
5642      * Changes to the width means we have at least 2 pages. Changes to the height
5643      * is done so we can check the changing of fr.rc.bottom.
5644      */
5645     fr.hdc = fr.hdcTarget = hdc;
5646     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5647     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5648     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5649 
5650     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5651     todo_wine {
5652     ok(r == len, "Expected %d, got %d\n", len, r);
5653     }
5654 
5655     /* We know that the page can't hold the full string. See how many characters
5656      * are on the first one
5657      */
5658     fr.chrg.cpMin = 0;
5659     fr.chrg.cpMax = -1;
5660     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5661     todo_wine {
5662     if (! skip_non_english)
5663       ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5664     }
5665     if (fmtstrings[i].first)
5666       todo_wine {
5667       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5668       }
5669     else
5670       ok(r < len, "Expected < %d, got %d\n", len, r);
5671 
5672     /* Do another page */
5673     fr.chrg.cpMin = r;
5674     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5675     if (fmtstrings[i].second)
5676       todo_wine {
5677       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5678       }
5679     else if (! skip_non_english)
5680       ok (r < len, "Expected < %d, got %d\n", len, r);
5681 
5682     /* There is at least on more page, but we don't care */
5683 
5684     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5685     todo_wine {
5686     ok(r == len, "Expected %d, got %d\n", len, r);
5687     }
5688   }
5689 
5690   ReleaseDC(NULL, hdc);
5691   DestroyWindow(hwndRichEdit);
5692 }
5693 
5694 static int nCallbackCount = 0;
5695 
5696 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5697 				 LONG cb, LONG* pcb)
5698 {
5699   const char text[] = {'t','e','s','t'};
5700 
5701   if (sizeof(text) <= cb)
5702   {
5703     if ((int)dwCookie != nCallbackCount)
5704     {
5705       *pcb = 0;
5706       return 0;
5707     }
5708 
5709     memcpy (pbBuff, text, sizeof(text));
5710     *pcb = sizeof(text);
5711 
5712     nCallbackCount++;
5713 
5714     return 0;
5715   }
5716   else
5717     return 1; /* indicates callback failed */
5718 }
5719 
5720 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5721                                          LPBYTE pbBuff,
5722                                          LONG cb,
5723                                          LONG *pcb)
5724 {
5725   const char** str = (const char**)dwCookie;
5726   int size = strlen(*str);
5727   *pcb = cb;
5728   if (*pcb > size) {
5729     *pcb = size;
5730   }
5731   if (*pcb > 0) {
5732     memcpy(pbBuff, *str, *pcb);
5733     *str += *pcb;
5734   }
5735   return 0;
5736 }
5737 
5738 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5739                                          LPBYTE pbBuff,
5740                                          LONG cb,
5741                                          LONG *pcb)
5742 {
5743     DWORD *phase = (DWORD *)dwCookie;
5744 
5745     if(*phase == 0){
5746         static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5747         *pcb = sizeof(first) - 1;
5748         memcpy(pbBuff, first, *pcb);
5749     }else if(*phase == 1){
5750         static const char second[] = "\x8f\xc3\x8b";
5751         *pcb = sizeof(second) - 1;
5752         memcpy(pbBuff, second, *pcb);
5753     }else
5754         *pcb = 0;
5755 
5756     ++*phase;
5757 
5758     return 0;
5759 }
5760 
5761 struct StringWithLength {
5762     int length;
5763     char *buffer;
5764 };
5765 
5766 /* This callback is used to handled the null characters in a string. */
5767 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5768                                                    LPBYTE pbBuff,
5769                                                    LONG cb,
5770                                                    LONG *pcb)
5771 {
5772     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5773     int size = str->length;
5774     *pcb = cb;
5775     if (*pcb > size) {
5776       *pcb = size;
5777     }
5778     if (*pcb > 0) {
5779       memcpy(pbBuff, str->buffer, *pcb);
5780       str->buffer += *pcb;
5781       str->length -= *pcb;
5782     }
5783     return 0;
5784 }
5785 
5786 static void test_EM_STREAMIN(void)
5787 {
5788   HWND hwndRichEdit = new_richedit(NULL);
5789   DWORD phase;
5790   LRESULT result;
5791   EDITSTREAM es;
5792   char buffer[1024] = {0}, tmp[16];
5793   CHARRANGE range;
5794   PARAFORMAT2 fmt;
5795 
5796   const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5797   const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5798   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5799   const char * ptr;
5800 
5801   const char * streamText1 =
5802   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5803   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5804   "}\r\n";
5805 
5806   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5807   const char * streamText2 =
5808     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5809     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5810     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5811     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5812     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5813     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5814     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5815 
5816   const char * streamText3 = "RichEdit1";
5817 
5818   const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5819 
5820   const char * streamText4 =
5821       "This text just needs to be long enough to cause run to be split onto "
5822       "two separate lines and make sure the null terminating character is "
5823       "handled properly.\0";
5824 
5825   const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5826 
5827   int length4 = strlen(streamText4) + 1;
5828   struct StringWithLength cookieForStream4 = {
5829       length4,
5830       (char *)streamText4,
5831   };
5832 
5833   const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5834   int length5 = sizeof(streamText5) / sizeof(WCHAR);
5835   struct StringWithLength cookieForStream5 = {
5836       sizeof(streamText5),
5837       (char *)streamText5,
5838   };
5839 
5840   /* Minimal test without \par at the end */
5841   es.dwCookie = (DWORD_PTR)&streamText0;
5842   es.dwError = 0;
5843   es.pfnCallback = test_EM_STREAMIN_esCallback;
5844   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5845   ok(result == 12, "got %ld, expected %d\n", result, 12);
5846 
5847   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5848   ok (result  == 12,
5849       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5850   result = strcmp (buffer,"TestSomeText");
5851   ok (result  == 0,
5852       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5853   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5854   /* Show that para fmts are ignored */
5855   range.cpMin = 2;
5856   range.cpMax = 2;
5857   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5858   memset(&fmt, 0xcc, sizeof(fmt));
5859   fmt.cbSize = sizeof(fmt);
5860   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5861   ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5862   ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5863   ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5864   ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5865 
5866   /* Native richedit 2.0 ignores last \par */
5867   ptr = streamText0a;
5868   es.dwCookie = (DWORD_PTR)&ptr;
5869   es.dwError = 0;
5870   es.pfnCallback = test_EM_STREAMIN_esCallback;
5871   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5872   ok(result == 12, "got %ld, expected %d\n", result, 12);
5873 
5874   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5875   ok (result  == 12,
5876       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5877   result = strcmp (buffer,"TestSomeText");
5878   ok (result  == 0,
5879       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5880   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5881   /* This time para fmts are processed */
5882   range.cpMin = 2;
5883   range.cpMax = 2;
5884   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5885   memset(&fmt, 0xcc, sizeof(fmt));
5886   fmt.cbSize = sizeof(fmt);
5887   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5888   ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5889   ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5890   ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5891   ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5892 
5893   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5894   es.dwCookie = (DWORD_PTR)&streamText0b;
5895   es.dwError = 0;
5896   es.pfnCallback = test_EM_STREAMIN_esCallback;
5897   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5898   ok(result == 13, "got %ld, expected %d\n", result, 13);
5899 
5900   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5901   ok (result  == 14,
5902       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5903   result = strcmp (buffer,"TestSomeText\r\n");
5904   ok (result  == 0,
5905       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5906   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5907 
5908   /* Show that when using SFF_SELECTION the last \par is not ignored. */
5909   ptr = streamText0a;
5910   es.dwCookie = (DWORD_PTR)&ptr;
5911   es.dwError = 0;
5912   es.pfnCallback = test_EM_STREAMIN_esCallback;
5913   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5914   ok(result == 12, "got %ld, expected %d\n", result, 12);
5915 
5916   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5917   ok (result  == 12,
5918       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5919   result = strcmp (buffer,"TestSomeText");
5920   ok (result  == 0,
5921       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5922   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5923 
5924   range.cpMin = 0;
5925   range.cpMax = -1;
5926   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5927   ok (result == 13, "got %ld\n", result);
5928 
5929   ptr = streamText0a;
5930   es.dwCookie = (DWORD_PTR)&ptr;
5931   es.dwError = 0;
5932   es.pfnCallback = test_EM_STREAMIN_esCallback;
5933 
5934   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5935   ok(result == 13, "got %ld, expected 13\n", result);
5936 
5937   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5938   ok (result  == 14,
5939       "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5940   result = strcmp (buffer,"TestSomeText\r\n");
5941   ok (result  == 0,
5942       "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5943   ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5944 
5945   es.dwCookie = (DWORD_PTR)&streamText1;
5946   es.dwError = 0;
5947   es.pfnCallback = test_EM_STREAMIN_esCallback;
5948   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5949   ok(result == 12, "got %ld, expected %d\n", result, 12);
5950 
5951   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5952   ok (result  == 12,
5953       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5954   result = strcmp (buffer,"TestSomeText");
5955   ok (result  == 0,
5956       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5957   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5958 
5959   es.dwCookie = (DWORD_PTR)&streamText2;
5960   es.dwError = 0;
5961   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5962   ok(result == 0, "got %ld, expected %d\n", result, 0);
5963 
5964   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5965   ok (result  == 0,
5966       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5967   ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5968   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5969 
5970   es.dwCookie = (DWORD_PTR)&streamText3;
5971   es.dwError = 0;
5972   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5973   ok(result == 0, "got %ld, expected %d\n", result, 0);
5974 
5975   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5976   ok (result  == 0,
5977       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5978   ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5979   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5980 
5981   es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5982   es.dwError = 0;
5983   es.pfnCallback = test_EM_STREAMIN_esCallback;
5984   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5985   ok(result == 18, "got %ld, expected %d\n", result, 18);
5986 
5987   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5988   ok(result  == 15,
5989       "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5990   result = strcmp (buffer,"TestUTF8WithBOM");
5991   ok(result  == 0,
5992       "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5993   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5994 
5995   phase = 0;
5996   es.dwCookie = (DWORD_PTR)&phase;
5997   es.dwError = 0;
5998   es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5999   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6000   ok(result == 8, "got %ld\n", result);
6001 
6002   WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
6003 
6004   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6005   ok(result  == 3,
6006       "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
6007   result = memcmp (buffer, tmp, 3);
6008   ok(result  == 0,
6009       "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
6010   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
6011 
6012   es.dwCookie = (DWORD_PTR)&cookieForStream4;
6013   es.dwError = 0;
6014   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6015   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6016   ok(result == length4, "got %ld, expected %d\n", result, length4);
6017 
6018   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6019   ok (result  == length4,
6020       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
6021   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
6022 
6023   es.dwCookie = (DWORD_PTR)&cookieForStream5;
6024   es.dwError = 0;
6025   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6026   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
6027   ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
6028 
6029   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6030   ok (result  == length5,
6031       "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
6032   ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
6033 
6034   DestroyWindow(hwndRichEdit);
6035 
6036   /* Single-line richedit */
6037   hwndRichEdit = new_richedit_with_style(NULL, 0);
6038   ptr = "line1\r\nline2";
6039   es.dwCookie = (DWORD_PTR)&ptr;
6040   es.dwError = 0;
6041   es.pfnCallback = test_EM_STREAMIN_esCallback;
6042   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6043   ok(result == 12, "got %ld, expected %d\n", result, 12);
6044   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6045   ok (!strcmp(buffer, "line1"),
6046       "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6047 }
6048 
6049 static void test_EM_StreamIn_Undo(void)
6050 {
6051   /* The purpose of this test is to determine when a EM_StreamIn should be
6052    * undoable. This is important because WM_PASTE currently uses StreamIn and
6053    * pasting should always be undoable but streaming isn't always.
6054    *
6055    * cases to test:
6056    * StreamIn plain text without SFF_SELECTION.
6057    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
6058    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
6059    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
6060    * Feel free to add tests for other text modes or StreamIn things.
6061    */
6062 
6063 
6064   HWND hwndRichEdit = new_richedit(NULL);
6065   LRESULT result;
6066   EDITSTREAM es;
6067   char buffer[1024] = {0};
6068   const char randomtext[] = "Some text";
6069 
6070   es.pfnCallback = EditStreamCallback;
6071 
6072   /* StreamIn, no SFF_SELECTION */
6073   es.dwCookie = nCallbackCount;
6074   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6075   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6076   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6077   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6078   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6079   result = strcmp (buffer,"test");
6080   ok (result  == 0,
6081       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6082 
6083   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6084   ok (result == FALSE,
6085       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
6086 
6087   /* StreamIn, SFF_SELECTION, but nothing selected */
6088   es.dwCookie = nCallbackCount;
6089   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6090   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6091   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6092   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6093   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6094   result = strcmp (buffer,"testSome text");
6095   ok (result  == 0,
6096       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6097 
6098   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6099   ok (result == TRUE,
6100      "EM_STREAMIN with SFF_SELECTION but no selection set "
6101       "should create an undo\n");
6102 
6103   /* StreamIn, SFF_SELECTION, with a selection */
6104   es.dwCookie = nCallbackCount;
6105   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6106   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6107   SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6108   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6109   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6110   result = strcmp (buffer,"Sometesttext");
6111   ok (result  == 0,
6112       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6113 
6114   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6115   ok (result == TRUE,
6116       "EM_STREAMIN with SFF_SELECTION and selection set "
6117       "should create an undo\n");
6118 
6119   DestroyWindow(hwndRichEdit);
6120 }
6121 
6122 static BOOL is_em_settextex_supported(HWND hwnd)
6123 {
6124     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6125     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6126 }
6127 
6128 static void test_unicode_conversions(void)
6129 {
6130     static const WCHAR tW[] = {'t',0};
6131     static const WCHAR teW[] = {'t','e',0};
6132     static const WCHAR textW[] = {'t','e','s','t',0};
6133     static const char textA[] = "test";
6134     char bufA[64];
6135     WCHAR bufW[64];
6136     HWND hwnd;
6137     int em_settextex_supported, ret;
6138 
6139 #define set_textA(hwnd, wm_set_text, txt) \
6140     do { \
6141         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6142         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6143         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6144         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6145         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
6146     } while(0)
6147 #define expect_textA(hwnd, wm_get_text, txt) \
6148     do { \
6149         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6150         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6151         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6152         memset(bufA, 0xAA, sizeof(bufA)); \
6153         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6154         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
6155         ret = lstrcmpA(bufA, txt); \
6156         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6157     } while(0)
6158 
6159 #define set_textW(hwnd, wm_set_text, txt) \
6160     do { \
6161         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6162         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6163         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6164         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6165         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
6166     } while(0)
6167 #define expect_textW(hwnd, wm_get_text, txt) \
6168     do { \
6169         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6170         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6171         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6172         memset(bufW, 0xAA, sizeof(bufW)); \
6173         ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6174         ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
6175         ret = lstrcmpW(bufW, txt); \
6176         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6177     } while(0)
6178 #define expect_empty(hwnd, wm_get_text) \
6179     do { \
6180         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6181         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6182         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6183         memset(bufA, 0xAA, sizeof(bufA)); \
6184         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6185         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6186         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6187     } while(0)
6188 
6189     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6190                            0, 0, 200, 60, 0, 0, 0, 0);
6191     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6192 
6193     ret = IsWindowUnicode(hwnd);
6194     ok(ret, "RichEdit20W should be unicode under NT\n");
6195 
6196     /* EM_SETTEXTEX is supported starting from version 3.0 */
6197     em_settextex_supported = is_em_settextex_supported(hwnd);
6198     trace("EM_SETTEXTEX is %ssupported on this platform\n",
6199           em_settextex_supported ? "" : "NOT ");
6200 
6201     expect_empty(hwnd, WM_GETTEXT);
6202     expect_empty(hwnd, EM_GETTEXTEX);
6203 
6204     ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6205     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6206     expect_textA(hwnd, WM_GETTEXT, "t");
6207     expect_textA(hwnd, EM_GETTEXTEX, "t");
6208     expect_textW(hwnd, EM_GETTEXTEX, tW);
6209 
6210     ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6211     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6212     expect_textA(hwnd, WM_GETTEXT, "te");
6213     expect_textA(hwnd, EM_GETTEXTEX, "te");
6214     expect_textW(hwnd, EM_GETTEXTEX, teW);
6215 
6216     set_textA(hwnd, WM_SETTEXT, NULL);
6217     expect_empty(hwnd, WM_GETTEXT);
6218     expect_empty(hwnd, EM_GETTEXTEX);
6219 
6220     set_textA(hwnd, WM_SETTEXT, textA);
6221     expect_textA(hwnd, WM_GETTEXT, textA);
6222     expect_textA(hwnd, EM_GETTEXTEX, textA);
6223     expect_textW(hwnd, EM_GETTEXTEX, textW);
6224 
6225     if (em_settextex_supported)
6226     {
6227         set_textA(hwnd, EM_SETTEXTEX, textA);
6228         expect_textA(hwnd, WM_GETTEXT, textA);
6229         expect_textA(hwnd, EM_GETTEXTEX, textA);
6230         expect_textW(hwnd, EM_GETTEXTEX, textW);
6231     }
6232 
6233     set_textW(hwnd, WM_SETTEXT, textW);
6234     expect_textW(hwnd, WM_GETTEXT, textW);
6235     expect_textA(hwnd, WM_GETTEXT, textA);
6236     expect_textW(hwnd, EM_GETTEXTEX, textW);
6237     expect_textA(hwnd, EM_GETTEXTEX, textA);
6238 
6239     if (em_settextex_supported)
6240     {
6241         set_textW(hwnd, EM_SETTEXTEX, textW);
6242         expect_textW(hwnd, WM_GETTEXT, textW);
6243         expect_textA(hwnd, WM_GETTEXT, textA);
6244         expect_textW(hwnd, EM_GETTEXTEX, textW);
6245         expect_textA(hwnd, EM_GETTEXTEX, textA);
6246     }
6247     DestroyWindow(hwnd);
6248 
6249     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6250                            0, 0, 200, 60, 0, 0, 0, 0);
6251     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6252 
6253     ret = IsWindowUnicode(hwnd);
6254     ok(!ret, "RichEdit20A should NOT be unicode\n");
6255 
6256     set_textA(hwnd, WM_SETTEXT, textA);
6257     expect_textA(hwnd, WM_GETTEXT, textA);
6258     expect_textA(hwnd, EM_GETTEXTEX, textA);
6259     expect_textW(hwnd, EM_GETTEXTEX, textW);
6260 
6261     if (em_settextex_supported)
6262     {
6263         set_textA(hwnd, EM_SETTEXTEX, textA);
6264         expect_textA(hwnd, WM_GETTEXT, textA);
6265         expect_textA(hwnd, EM_GETTEXTEX, textA);
6266         expect_textW(hwnd, EM_GETTEXTEX, textW);
6267     }
6268 
6269         set_textW(hwnd, WM_SETTEXT, textW);
6270         expect_textW(hwnd, WM_GETTEXT, textW);
6271         expect_textA(hwnd, WM_GETTEXT, textA);
6272         expect_textW(hwnd, EM_GETTEXTEX, textW);
6273         expect_textA(hwnd, EM_GETTEXTEX, textA);
6274 
6275     if (em_settextex_supported)
6276     {
6277         set_textW(hwnd, EM_SETTEXTEX, textW);
6278         expect_textW(hwnd, WM_GETTEXT, textW);
6279         expect_textA(hwnd, WM_GETTEXT, textA);
6280         expect_textW(hwnd, EM_GETTEXTEX, textW);
6281         expect_textA(hwnd, EM_GETTEXTEX, textA);
6282     }
6283     DestroyWindow(hwnd);
6284 }
6285 
6286 static void test_WM_CHAR(void)
6287 {
6288     HWND hwnd;
6289     int ret;
6290     const char * char_list = "abc\rabc\r";
6291     const char * expected_content_single = "abcabc";
6292     const char * expected_content_multi = "abc\r\nabc\r\n";
6293     char buffer[64] = {0};
6294     const char * p;
6295 
6296     /* single-line control must IGNORE carriage returns */
6297     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6298                            0, 0, 200, 60, 0, 0, 0, 0);
6299     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6300 
6301     p = char_list;
6302     while (*p != '\0') {
6303         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6304         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6305         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6306         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6307         p++;
6308     }
6309 
6310     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6311     ret = strcmp(buffer, expected_content_single);
6312     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6313 
6314     DestroyWindow(hwnd);
6315 
6316     /* multi-line control inserts CR normally */
6317     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6318                            0, 0, 200, 60, 0, 0, 0, 0);
6319     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6320 
6321     p = char_list;
6322     while (*p != '\0') {
6323         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6324         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6325         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6326         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6327         p++;
6328     }
6329 
6330     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6331     ret = strcmp(buffer, expected_content_multi);
6332     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6333 
6334     DestroyWindow(hwnd);
6335 }
6336 
6337 static void test_EM_GETTEXTLENGTHEX(void)
6338 {
6339     HWND hwnd;
6340     GETTEXTLENGTHEX gtl;
6341     int ret;
6342     const char * base_string = "base string";
6343     const char * test_string = "a\nb\n\n\r\n";
6344     const char * test_string_after = "a";
6345     const char * test_string_2 = "a\rtest\rstring";
6346     char buffer[64] = {0};
6347 
6348     /* single line */
6349     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6350                            0, 0, 200, 60, 0, 0, 0, 0);
6351     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6352 
6353     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6354     gtl.codepage = CP_ACP;
6355     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6356     ok(ret == 0, "ret %d\n",ret);
6357 
6358     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6359     gtl.codepage = CP_ACP;
6360     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6361     ok(ret == 0, "ret %d\n",ret);
6362 
6363     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6364 
6365     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6366     gtl.codepage = CP_ACP;
6367     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6368     ok(ret == strlen(base_string), "ret %d\n",ret);
6369 
6370     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6371     gtl.codepage = CP_ACP;
6372     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6373     ok(ret == strlen(base_string), "ret %d\n",ret);
6374 
6375     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6376 
6377     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6378     gtl.codepage = CP_ACP;
6379     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6380     ok(ret == 1, "ret %d\n",ret);
6381 
6382     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6383     gtl.codepage = CP_ACP;
6384     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6385     ok(ret == 1, "ret %d\n",ret);
6386 
6387     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6388     ret = strcmp(buffer, test_string_after);
6389     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6390 
6391     DestroyWindow(hwnd);
6392 
6393     /* multi line */
6394     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6395                            0, 0, 200, 60, 0, 0, 0, 0);
6396     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6397 
6398     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6399     gtl.codepage = CP_ACP;
6400     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6401     ok(ret == 0, "ret %d\n",ret);
6402 
6403     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6404     gtl.codepage = CP_ACP;
6405     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6406     ok(ret == 0, "ret %d\n",ret);
6407 
6408     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6409 
6410     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6411     gtl.codepage = CP_ACP;
6412     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6413     ok(ret == strlen(base_string), "ret %d\n",ret);
6414 
6415     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6416     gtl.codepage = CP_ACP;
6417     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6418     ok(ret == strlen(base_string), "ret %d\n",ret);
6419 
6420     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6421 
6422     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6423     gtl.codepage = CP_ACP;
6424     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6425     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6426 
6427     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6428     gtl.codepage = CP_ACP;
6429     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6430     ok(ret == strlen(test_string_2), "ret %d\n",ret);
6431 
6432     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6433 
6434     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6435     gtl.codepage = CP_ACP;
6436     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6437     ok(ret == 10, "ret %d\n",ret);
6438 
6439     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6440     gtl.codepage = CP_ACP;
6441     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6442     ok(ret == 6, "ret %d\n",ret);
6443 
6444     /* Unicode/NUMCHARS/NUMBYTES */
6445     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6446 
6447     gtl.flags = GTL_DEFAULT;
6448     gtl.codepage = 1200;
6449     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6450     ok(ret == lstrlenA(test_string_2),
6451        "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6452 
6453     gtl.flags = GTL_NUMCHARS;
6454     gtl.codepage = 1200;
6455     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6456     ok(ret == lstrlenA(test_string_2),
6457        "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6458 
6459     gtl.flags = GTL_NUMBYTES;
6460     gtl.codepage = 1200;
6461     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6462     ok(ret == lstrlenA(test_string_2)*2,
6463        "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6464 
6465     gtl.flags = GTL_PRECISE;
6466     gtl.codepage = 1200;
6467     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6468     ok(ret == lstrlenA(test_string_2)*2,
6469        "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6470 
6471     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6472     gtl.codepage = 1200;
6473     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6474     ok(ret == lstrlenA(test_string_2),
6475        "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6476 
6477     gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6478     gtl.codepage = 1200;
6479     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6480     ok(ret == E_INVALIDARG,
6481        "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6482 
6483     DestroyWindow(hwnd);
6484 }
6485 
6486 
6487 /* globals that parent and child access when checking event masks & notifications */
6488 static HWND eventMaskEditHwnd = 0;
6489 static int queriedEventMask;
6490 static int watchForEventMask = 0;
6491 
6492 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6493 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6494 {
6495     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6496     {
6497       queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6498     }
6499     return DefWindowProcA(hwnd, message, wParam, lParam);
6500 }
6501 
6502 /* test event masks in combination with WM_COMMAND */
6503 static void test_eventMask(void)
6504 {
6505     HWND parent;
6506     int ret, style;
6507     WNDCLASSA cls;
6508     const char text[] = "foo bar\n";
6509     int eventMask;
6510 
6511     /* register class to capture WM_COMMAND */
6512     cls.style = 0;
6513     cls.lpfnWndProc = ParentMsgCheckProcA;
6514     cls.cbClsExtra = 0;
6515     cls.cbWndExtra = 0;
6516     cls.hInstance = GetModuleHandleA(0);
6517     cls.hIcon = 0;
6518     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6519     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6520     cls.lpszMenuName = NULL;
6521     cls.lpszClassName = "EventMaskParentClass";
6522     if(!RegisterClassA(&cls)) assert(0);
6523 
6524     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6525                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6526     ok (parent != 0, "Failed to create parent window\n");
6527 
6528     eventMaskEditHwnd = new_richedit(parent);
6529     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6530 
6531     eventMask = ENM_CHANGE | ENM_UPDATE;
6532     ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6533     ok(ret == ENM_NONE, "wrong event mask\n");
6534     ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6535     ok(ret == eventMask, "failed to set event mask\n");
6536 
6537     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6538     queriedEventMask = 0;  /* initialize to something other than we expect */
6539     watchForEventMask = EN_CHANGE;
6540     ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6541     ok(ret == TRUE, "failed to set text\n");
6542     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6543        notification in response to WM_SETTEXT */
6544     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6545             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6546 
6547     /* check to see if EN_CHANGE is sent when redraw is turned off */
6548     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6549     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6550     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6551     /* redraw is disabled by making the window invisible. */
6552     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6553     queriedEventMask = 0;  /* initialize to something other than we expect */
6554     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6555     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6556             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6557     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6558     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6559 
6560     /* check to see if EN_UPDATE is sent when the editor isn't visible */
6561     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6562     style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6563     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6564     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6565     watchForEventMask = EN_UPDATE;
6566     queriedEventMask = 0;  /* initialize to something other than we expect */
6567     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6568     ok(queriedEventMask == 0,
6569             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6570     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6571     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6572     queriedEventMask = 0;  /* initialize to something other than we expect */
6573     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6574     ok(queriedEventMask == eventMask,
6575             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6576 
6577 
6578     DestroyWindow(parent);
6579 }
6580 
6581 static int received_WM_NOTIFY = 0;
6582 static int modify_at_WM_NOTIFY = 0;
6583 static BOOL filter_on_WM_NOTIFY = FALSE;
6584 static HWND hwndRichedit_WM_NOTIFY;
6585 
6586 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6587 {
6588     if(message == WM_NOTIFY)
6589     {
6590       received_WM_NOTIFY = 1;
6591       modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6592       if (filter_on_WM_NOTIFY) return TRUE;
6593     }
6594     return DefWindowProcA(hwnd, message, wParam, lParam);
6595 }
6596 
6597 static void test_WM_NOTIFY(void)
6598 {
6599     HWND parent;
6600     WNDCLASSA cls;
6601     CHARFORMAT2A cf2;
6602     int sel_start, sel_end;
6603 
6604     /* register class to capture WM_NOTIFY */
6605     cls.style = 0;
6606     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6607     cls.cbClsExtra = 0;
6608     cls.cbWndExtra = 0;
6609     cls.hInstance = GetModuleHandleA(0);
6610     cls.hIcon = 0;
6611     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6612     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6613     cls.lpszMenuName = NULL;
6614     cls.lpszClassName = "WM_NOTIFY_ParentClass";
6615     if(!RegisterClassA(&cls)) assert(0);
6616 
6617     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6618                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6619     ok (parent != 0, "Failed to create parent window\n");
6620 
6621     hwndRichedit_WM_NOTIFY = new_richedit(parent);
6622     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6623 
6624     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6625 
6626     /* Notifications for selection change should only be sent when selection
6627        actually changes. EM_SETCHARFORMAT is one message that calls
6628        ME_CommitUndo, which should check whether message should be sent */
6629     received_WM_NOTIFY = 0;
6630     cf2.cbSize = sizeof(CHARFORMAT2A);
6631     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6632     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6633     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6634     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6635     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6636 
6637     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6638        already at 0. */
6639     received_WM_NOTIFY = 0;
6640     modify_at_WM_NOTIFY = 0;
6641     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6642     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6643     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6644 
6645     received_WM_NOTIFY = 0;
6646     modify_at_WM_NOTIFY = 0;
6647     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6648     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6649 
6650     received_WM_NOTIFY = 0;
6651     modify_at_WM_NOTIFY = 0;
6652     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6653     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6654     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6655 
6656     /* Test for WM_NOTIFY messages with redraw disabled. */
6657     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6658     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6659     received_WM_NOTIFY = 0;
6660     SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6661     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6662     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6663 
6664     /* Test filtering key events. */
6665     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6666     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6667     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6668     received_WM_NOTIFY = 0;
6669     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6670     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6671     ok(sel_start == 1 && sel_end == 1,
6672        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6673     filter_on_WM_NOTIFY = TRUE;
6674     received_WM_NOTIFY = 0;
6675     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6676     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6677     ok(sel_start == 1 && sel_end == 1,
6678        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6679 
6680     /* test with owner set to NULL */
6681     SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6682     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6683     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6684     ok(sel_start == 1 && sel_end == 1,
6685        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6686 
6687     DestroyWindow(hwndRichedit_WM_NOTIFY);
6688     DestroyWindow(parent);
6689 }
6690 
6691 static ENLINK enlink;
6692 #define CURSOR_CLIENT_X 5
6693 #define CURSOR_CLIENT_Y 5
6694 #define WP_PARENT 1
6695 #define WP_CHILD 2
6696 
6697 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6698 {
6699     if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6700     {
6701         enlink = *(ENLINK*)lParam;
6702     }
6703     return DefWindowProcA(hwnd, message, wParam, lParam);
6704 }
6705 
6706 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6707                              UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6708 {
6709     ENLINK junk_enlink;
6710 
6711     switch (msg)
6712     {
6713     case WM_LBUTTONDBLCLK:
6714     case WM_LBUTTONDOWN:
6715     case WM_LBUTTONUP:
6716     case WM_MOUSEHOVER:
6717     case WM_MOUSEMOVE:
6718     case WM_MOUSEWHEEL:
6719     case WM_RBUTTONDBLCLK:
6720     case WM_RBUTTONDOWN:
6721     case WM_RBUTTONUP:
6722         lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6723         break;
6724     case WM_SETCURSOR:
6725         if (wParam == WP_PARENT)
6726             wParam = (WPARAM)parent;
6727         else if (wParam == WP_CHILD)
6728             wParam = (WPARAM)hwnd;
6729         break;
6730     }
6731 
6732     memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6733     enlink = junk_enlink;
6734 
6735     SendMessageA(hwnd, msg, wParam, lParam);
6736 
6737     if (notifies)
6738     {
6739         ok(enlink.nmhdr.hwndFrom == hwnd,
6740            "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6741         ok(enlink.nmhdr.idFrom == 0,
6742            "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6743         ok(enlink.msg == msg,
6744            "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6745         if (msg == WM_SETCURSOR)
6746         {
6747             ok(enlink.wParam == 0,
6748                "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6749         }
6750         else
6751         {
6752             ok(enlink.wParam == wParam,
6753                "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6754         }
6755         ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6756            "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6757            desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6758         ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6759            "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6760     }
6761     else
6762     {
6763         ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6764            "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6765     }
6766 }
6767 
6768 static void test_EN_LINK(void)
6769 {
6770     HWND hwnd, parent;
6771     WNDCLASSA cls;
6772     CHARFORMAT2A cf2;
6773     POINT orig_cursor_pos;
6774     POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6775     int i;
6776 
6777     static const struct
6778     {
6779         UINT msg;
6780         WPARAM wParam;
6781         LPARAM lParam;
6782         BOOL notifies;
6783     }
6784     link_notify_tests[] =
6785     {
6786         /* hold down the left button and try some messages */
6787         { WM_LBUTTONDOWN,    0,          0,  TRUE  }, /* 0 */
6788         { EM_LINESCROLL,     0,          1,  FALSE },
6789         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6790         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6791         { WM_MOUSEHOVER,     0,          0,  FALSE },
6792         { WM_MOUSEMOVE,      0,          0,  FALSE },
6793         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6794         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6795         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6796         { WM_RBUTTONUP,      0,          0,  TRUE  },
6797         { WM_SETCURSOR,      0,          0,  FALSE },
6798         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6799         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6800         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6801         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6802         { WM_LBUTTONUP,      0,          0,  TRUE  },
6803         /* hold down the right button and try some messages */
6804         { WM_RBUTTONDOWN,    0,          0,  TRUE  }, /* 16 */
6805         { EM_LINESCROLL,     0,          1,  FALSE },
6806         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6807         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6808         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6809         { WM_LBUTTONUP,      0,          0,  TRUE  },
6810         { WM_MOUSEHOVER,     0,          0,  FALSE },
6811         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6812         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6813         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6814         { WM_SETCURSOR,      0,          0,  FALSE },
6815         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6816         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6817         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6818         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6819         { WM_RBUTTONUP,      0,          0,  TRUE  },
6820         /* try the messages with both buttons released */
6821         { EM_LINESCROLL,     0,          1,  FALSE }, /* 32 */
6822         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6823         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6824         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6825         { WM_LBUTTONUP,      0,          0,  TRUE  },
6826         { WM_MOUSEHOVER,     0,          0,  FALSE },
6827         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6828         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6829         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6830         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6831         { WM_RBUTTONUP,      0,          0,  TRUE  },
6832         { WM_SETCURSOR,      0,          0,  FALSE },
6833         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6834         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6835         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6836         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE }
6837     };
6838 
6839     /* register class to capture WM_NOTIFY */
6840     cls.style = 0;
6841     cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6842     cls.cbClsExtra = 0;
6843     cls.cbWndExtra = 0;
6844     cls.hInstance = GetModuleHandleA(0);
6845     cls.hIcon = 0;
6846     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6847     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6848     cls.lpszMenuName = NULL;
6849     cls.lpszClassName = "EN_LINK_ParentClass";
6850     if(!RegisterClassA(&cls)) assert(0);
6851 
6852     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6853                            0, 0, 200, 60, NULL, NULL, NULL, NULL);
6854     ok(parent != 0, "Failed to create parent window\n");
6855 
6856     hwnd = new_richedit(parent);
6857     ok(hwnd != 0, "Failed to create edit window\n");
6858 
6859     SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6860 
6861     cf2.cbSize = sizeof(CHARFORMAT2A);
6862     cf2.dwMask = CFM_LINK;
6863     cf2.dwEffects = CFE_LINK;
6864     SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6865     /* mixing letters and numbers causes runs to be split */
6866     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6867 
6868     GetCursorPos(&orig_cursor_pos);
6869     SetCursorPos(0, 0);
6870 
6871     for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6872     {
6873         link_notify_test("cursor position simulated", i, hwnd, parent,
6874                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6875                          link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6876     }
6877 
6878     ClientToScreen(hwnd, &cursor_screen_pos);
6879     SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6880 
6881     for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6882     {
6883         link_notify_test("cursor position set", i, hwnd, parent,
6884                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6885                          link_notify_tests[i].notifies);
6886     }
6887 
6888     SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6889     DestroyWindow(hwnd);
6890     DestroyWindow(parent);
6891 }
6892 
6893 static void test_undo_coalescing(void)
6894 {
6895     HWND hwnd;
6896     int result;
6897     char buffer[64] = {0};
6898 
6899     /* multi-line control inserts CR normally */
6900     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6901                            0, 0, 200, 60, 0, 0, 0, 0);
6902     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6903 
6904     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6905     ok (result == FALSE, "Can undo after window creation.\n");
6906     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6907     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6908     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6909     ok (result == FALSE, "Can redo after window creation.\n");
6910     result = SendMessageA(hwnd, EM_REDO, 0, 0);
6911     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6912 
6913     /* Test the effect of arrows keys during typing on undo transactions*/
6914     simulate_typing_characters(hwnd, "one two three");
6915     SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6916     SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6917     simulate_typing_characters(hwnd, " four five six");
6918 
6919     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6920     ok (result == FALSE, "Can redo before anything is undone.\n");
6921     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6922     ok (result == TRUE, "Cannot undo typed characters.\n");
6923     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6924     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6925     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6926     ok (result == TRUE, "Cannot redo after undo.\n");
6927     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6928     result = strcmp(buffer, "one two three");
6929     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6930 
6931     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6932     ok (result == TRUE, "Cannot undo typed characters.\n");
6933     result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6934     ok (result == TRUE, "Failed to undo typed characters.\n");
6935     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6936     result = strcmp(buffer, "");
6937     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6938 
6939     /* Test the effect of focus changes during typing on undo transactions*/
6940     simulate_typing_characters(hwnd, "one two three");
6941     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6942     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6943     SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6944     SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6945     simulate_typing_characters(hwnd, " four five six");
6946     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6947     ok (result == TRUE, "Failed to undo typed characters.\n");
6948     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6949     result = strcmp(buffer, "one two three");
6950     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6951 
6952     /* Test the effect of the back key during typing on undo transactions */
6953     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6954     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6955     ok (result == TRUE, "Failed to clear the text.\n");
6956     simulate_typing_characters(hwnd, "one two threa");
6957     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6958     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6959     SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6960     SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6961     simulate_typing_characters(hwnd, "e four five six");
6962     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6963     ok (result == TRUE, "Failed to undo typed characters.\n");
6964     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6965     result = strcmp(buffer, "");
6966     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6967 
6968     /* Test the effect of the delete key during typing on undo transactions */
6969     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6970     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6971     ok(result == TRUE, "Failed to set the text.\n");
6972     SendMessageA(hwnd, EM_SETSEL, 1, 1);
6973     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6974     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6975     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6976     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6977     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6978     ok (result == TRUE, "Failed to undo typed characters.\n");
6979     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6980     result = strcmp(buffer, "acd");
6981     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6982     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6983     ok (result == TRUE, "Failed to undo typed characters.\n");
6984     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6985     result = strcmp(buffer, "abcd");
6986     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6987 
6988     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6989     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6990     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6991     ok (result == TRUE, "Failed to clear the text.\n");
6992     simulate_typing_characters(hwnd, "one two three");
6993     result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6994     ok (result == 0, "expected %d but got %d\n", 0, result);
6995     simulate_typing_characters(hwnd, " four five six");
6996     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6997     ok (result == TRUE, "Failed to undo typed characters.\n");
6998     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6999     result = strcmp(buffer, "one two three");
7000     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7001     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7002     ok (result == TRUE, "Failed to undo typed characters.\n");
7003     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7004     result = strcmp(buffer, "");
7005     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7006 
7007     DestroyWindow(hwnd);
7008 }
7009 
7010 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
7011 {
7012     int length;
7013 
7014     /* MSDN lied, length is actually the number of bytes. */
7015     length = bytes / sizeof(WCHAR);
7016     switch(code)
7017     {
7018         case WB_ISDELIMITER:
7019             return text[pos] == 'X';
7020         case WB_LEFT:
7021         case WB_MOVEWORDLEFT:
7022             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7023                 return pos-1;
7024             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
7025         case WB_LEFTBREAK:
7026             pos--;
7027             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7028                 pos--;
7029             return pos;
7030         case WB_RIGHT:
7031         case WB_MOVEWORDRIGHT:
7032             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7033                 return pos+1;
7034             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
7035         case WB_RIGHTBREAK:
7036             pos++;
7037             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7038                 pos++;
7039             return pos;
7040         default:
7041             ok(FALSE, "Unexpected code %d\n", code);
7042             break;
7043     }
7044     return 0;
7045 }
7046 
7047 static void test_word_movement(void)
7048 {
7049     HWND hwnd;
7050     int result;
7051     int sel_start, sel_end;
7052     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
7053 
7054     /* multi-line control inserts CR normally */
7055     hwnd = new_richedit(NULL);
7056 
7057     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
7058     ok (result == TRUE, "Failed to clear the text.\n");
7059     SendMessageA(hwnd, EM_SETSEL, 0, 0);
7060     /* |one two three */
7061 
7062     send_ctrl_key(hwnd, VK_RIGHT);
7063     /* one |two  three */
7064     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7065     ok(sel_start == sel_end, "Selection should be empty\n");
7066     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7067 
7068     send_ctrl_key(hwnd, VK_RIGHT);
7069     /* one two  |three */
7070     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7071     ok(sel_start == sel_end, "Selection should be empty\n");
7072     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7073 
7074     send_ctrl_key(hwnd, VK_LEFT);
7075     /* one |two  three */
7076     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7077     ok(sel_start == sel_end, "Selection should be empty\n");
7078     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7079 
7080     send_ctrl_key(hwnd, VK_LEFT);
7081     /* |one two  three */
7082     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7083     ok(sel_start == sel_end, "Selection should be empty\n");
7084     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
7085 
7086     SendMessageA(hwnd, EM_SETSEL, 8, 8);
7087     /* one two | three */
7088     send_ctrl_key(hwnd, VK_RIGHT);
7089     /* one two  |three */
7090     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7091     ok(sel_start == sel_end, "Selection should be empty\n");
7092     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7093 
7094     SendMessageA(hwnd, EM_SETSEL, 11, 11);
7095     /* one two  th|ree */
7096     send_ctrl_key(hwnd, VK_LEFT);
7097     /* one two  |three */
7098     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7099     ok(sel_start == sel_end, "Selection should be empty\n");
7100     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7101 
7102     /* Test with a custom word break procedure that uses X as the delimiter. */
7103     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
7104     ok (result == TRUE, "Failed to clear the text.\n");
7105     SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7106     /* |one twoXthree */
7107     send_ctrl_key(hwnd, VK_RIGHT);
7108     /* one twoX|three */
7109     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7110     ok(sel_start == sel_end, "Selection should be empty\n");
7111     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7112 
7113     DestroyWindow(hwnd);
7114 
7115     /* Make sure the behaviour is the same with a unicode richedit window,
7116      * and using unicode functions. */
7117 
7118     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7119                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7120                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7121 
7122     /* Test with a custom word break procedure that uses X as the delimiter. */
7123     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7124     ok (result == TRUE, "Failed to clear the text.\n");
7125     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7126     /* |one twoXthree */
7127     send_ctrl_key(hwnd, VK_RIGHT);
7128     /* one twoX|three */
7129     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7130     ok(sel_start == sel_end, "Selection should be empty\n");
7131     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7132 
7133     DestroyWindow(hwnd);
7134 }
7135 
7136 static void test_EM_CHARFROMPOS(void)
7137 {
7138     HWND hwnd;
7139     int result;
7140     RECT rcClient;
7141     POINTL point;
7142     point.x = 0;
7143     point.y = 40;
7144 
7145     /* multi-line control inserts CR normally */
7146     hwnd = new_richedit(NULL);
7147     result = SendMessageA(hwnd, WM_SETTEXT, 0,
7148                           (LPARAM)"one two three four five six seven\reight");
7149     ok(result == 1, "Expected 1, got %d\n", result);
7150     GetClientRect(hwnd, &rcClient);
7151 
7152     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7153     ok(result == 34, "expected character index of 34 but got %d\n", result);
7154 
7155     /* Test with points outside the bounds of the richedit control. */
7156     point.x = -1;
7157     point.y = 40;
7158     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7159     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7160 
7161     point.x = 1000;
7162     point.y = 0;
7163     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7164     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
7165 
7166     point.x = 1000;
7167     point.y = 36;
7168     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7169     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7170 
7171     point.x = 1000;
7172     point.y = -1;
7173     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7174     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7175 
7176     point.x = 1000;
7177     point.y = rcClient.bottom + 1;
7178     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7179     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7180 
7181     point.x = 1000;
7182     point.y = rcClient.bottom;
7183     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7184     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7185 
7186     DestroyWindow(hwnd);
7187 }
7188 
7189 static void test_word_wrap(void)
7190 {
7191     HWND hwnd;
7192     POINTL point = {0, 60}; /* This point must be below the first line */
7193     const char *text = "Must be long enough to test line wrapping";
7194     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7195     int res, pos, lines;
7196 
7197     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7198      * when specified on window creation and set later. */
7199     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7200                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7201     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7202     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7203     ok(res, "WM_SETTEXT failed.\n");
7204     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7205     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7206     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7207     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7208 
7209     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
7210     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7211     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7212     DestroyWindow(hwnd);
7213 
7214     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7215                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7216     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7217 
7218     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7219     ok(res, "WM_SETTEXT failed.\n");
7220     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7221     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7222     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7223     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7224 
7225     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7226     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7227     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7228     DestroyWindow(hwnd);
7229 
7230     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7231                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7232     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7233     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7234     ok(res, "WM_SETTEXT failed.\n");
7235     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7236     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7237 
7238     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7239     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7240     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7241     DestroyWindow(hwnd);
7242 
7243     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7244                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7245                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7246     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7247     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7248     ok(res, "WM_SETTEXT failed.\n");
7249     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7250     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7251 
7252     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7253     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7254     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7255 
7256     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7257     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7258     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7259     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7260     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7261 
7262     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7263     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7264     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7265     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7266     DestroyWindow(hwnd);
7267 
7268     /* Test to see if wrapping happens with redraw disabled. */
7269     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7270                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7271     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7272     SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7273     res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7274     ok(res, "EM_REPLACESEL failed.\n");
7275     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7276     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7277     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7278     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7279     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7280 
7281     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7282     DestroyWindow(hwnd);
7283 }
7284 
7285 static void test_autoscroll(void)
7286 {
7287     HWND hwnd = new_richedit(NULL);
7288     int lines, ret, redraw;
7289     POINT pt;
7290 
7291     for (redraw = 0; redraw <= 1; redraw++) {
7292         trace("testing with WM_SETREDRAW=%d\n", redraw);
7293         SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7294         SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7295         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7296         ok(lines == 8, "%d lines instead of 8\n", lines);
7297         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7298         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7299         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7300         ret = GetWindowLongA(hwnd, GWL_STYLE);
7301         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7302 
7303         SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7304         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7305         ok(lines == 1, "%d lines instead of 1\n", lines);
7306         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7307         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7308         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7309         ret = GetWindowLongA(hwnd, GWL_STYLE);
7310         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7311     }
7312 
7313     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7314     DestroyWindow(hwnd);
7315 
7316     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7317      * auto vertical/horizontal scrolling options. */
7318     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7319                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7320                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7321     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7322     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7323     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7324     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7325     ret = GetWindowLongA(hwnd, GWL_STYLE);
7326     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7327     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7328     DestroyWindow(hwnd);
7329 
7330     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7331                           WS_POPUP|ES_MULTILINE,
7332                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7333     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7334     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7335     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7336     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7337     ret = GetWindowLongA(hwnd, GWL_STYLE);
7338     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7339     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7340     DestroyWindow(hwnd);
7341 }
7342 
7343 
7344 static void test_format_rect(void)
7345 {
7346     HWND hwnd;
7347     RECT rc, expected, clientRect;
7348     int n;
7349     DWORD options;
7350 
7351     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7352                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7353                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7354     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7355 
7356     GetClientRect(hwnd, &clientRect);
7357 
7358     expected = clientRect;
7359     InflateRect(&expected, -1, 0);
7360     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7361     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7362        wine_dbgstr_rect(&expected));
7363 
7364     for (n = -3; n <= 3; n++)
7365     {
7366       rc = clientRect;
7367       InflateRect(&rc, -n, -n);
7368       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7369 
7370       expected = rc;
7371       expected.top = max(0, rc.top);
7372       expected.left = max(0, rc.left);
7373       expected.bottom = min(clientRect.bottom, rc.bottom);
7374       expected.right = min(clientRect.right, rc.right);
7375       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7376       ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7377          wine_dbgstr_rect(&expected));
7378     }
7379 
7380     rc = clientRect;
7381     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7382     expected = clientRect;
7383     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7384     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7385        wine_dbgstr_rect(&expected));
7386 
7387     /* Adding the selectionbar adds the selectionbar width to the left side. */
7388     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7389     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7390     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7391     expected.left += 8; /* selection bar width */
7392     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7393     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7394        wine_dbgstr_rect(&expected));
7395 
7396     rc = clientRect;
7397     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7398     expected = clientRect;
7399     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7400     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7401        wine_dbgstr_rect(&expected));
7402 
7403     /* Removing the selectionbar subtracts the selectionbar width from the left side,
7404      * even if the left side is already 0. */
7405     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7406     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7407     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7408     expected.left -= 8; /* selection bar width */
7409     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7410     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7411        wine_dbgstr_rect(&expected));
7412 
7413     /* Set the absolute value of the formatting rectangle. */
7414     rc = clientRect;
7415     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7416     expected = clientRect;
7417     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7418     ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7419        wine_dbgstr_rect(&expected));
7420 
7421     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7422      * LPARAM as being a relative offset when the WPARAM value is 1, but these
7423      * tests show that this isn't true. */
7424     rc.top = 15;
7425     rc.left = 15;
7426     rc.bottom = clientRect.bottom - 15;
7427     rc.right = clientRect.right - 15;
7428     expected = rc;
7429     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7430     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7431     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7432        wine_dbgstr_rect(&expected));
7433 
7434     /* For some reason it does not limit the values to the client rect with
7435      * a WPARAM value of 1. */
7436     rc.top = -15;
7437     rc.left = -15;
7438     rc.bottom = clientRect.bottom + 15;
7439     rc.right = clientRect.right + 15;
7440     expected = rc;
7441     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7442     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7443     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7444        wine_dbgstr_rect(&expected));
7445 
7446     /* Reset to default rect and check how the format rect adjusts to window
7447      * resize and how it copes with very small windows */
7448     SendMessageA(hwnd, EM_SETRECT, 0, 0);
7449 
7450     MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7451     GetClientRect(hwnd, &clientRect);
7452 
7453     expected = clientRect;
7454     InflateRect(&expected, -1, 0);
7455     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7456     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7457        wine_dbgstr_rect(&expected));
7458 
7459     MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7460     GetClientRect(hwnd, &clientRect);
7461 
7462     expected = clientRect;
7463     InflateRect(&expected, -1, 0);
7464     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7465     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7466        wine_dbgstr_rect(&expected));
7467 
7468     MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7469     GetClientRect(hwnd, &clientRect);
7470 
7471     expected = clientRect;
7472     InflateRect(&expected, -1, 0);
7473     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7474     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7475        wine_dbgstr_rect(&expected));
7476 
7477     DestroyWindow(hwnd);
7478 
7479     /* The extended window style affects the formatting rectangle. */
7480     hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7481                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7482                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7483     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7484 
7485     GetClientRect(hwnd, &clientRect);
7486 
7487     expected = clientRect;
7488     expected.top += 1;
7489     InflateRect(&expected, -1, 0);
7490     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7491     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7492        wine_dbgstr_rect(&expected));
7493 
7494     rc = clientRect;
7495     InflateRect(&rc, -5, -5);
7496     expected = rc;
7497     expected.top -= 1;
7498     InflateRect(&expected, 1, 0);
7499     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7500     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7501     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7502        wine_dbgstr_rect(&expected));
7503 
7504     DestroyWindow(hwnd);
7505 }
7506 
7507 static void test_WM_GETDLGCODE(void)
7508 {
7509     HWND hwnd;
7510     UINT res, expected;
7511     MSG msg;
7512 
7513     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7514 
7515     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7516                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7517                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7518     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7519     msg.hwnd = hwnd;
7520     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7521     expected = expected | DLGC_WANTMESSAGE;
7522     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7523        res, expected);
7524     DestroyWindow(hwnd);
7525 
7526     msg.message = WM_KEYDOWN;
7527     msg.wParam = VK_RETURN;
7528     msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7529     msg.pt.x = 0;
7530     msg.pt.y = 0;
7531     msg.time = GetTickCount();
7532 
7533     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7534                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7535                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7536     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7537     msg.hwnd = hwnd;
7538     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7539     expected = expected | DLGC_WANTMESSAGE;
7540     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7541        res, expected);
7542     DestroyWindow(hwnd);
7543 
7544     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7545                           ES_MULTILINE|WS_POPUP,
7546                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7547     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7548     msg.hwnd = hwnd;
7549     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7550     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7551     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7552        res, expected);
7553     DestroyWindow(hwnd);
7554 
7555     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7556                           ES_WANTRETURN|WS_POPUP,
7557                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7558     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7559     msg.hwnd = hwnd;
7560     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7561     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7562     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7563        res, expected);
7564     DestroyWindow(hwnd);
7565 
7566     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7567                           WS_POPUP,
7568                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7569     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7570     msg.hwnd = hwnd;
7571     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7572     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7573     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7574        res, expected);
7575     DestroyWindow(hwnd);
7576 
7577     msg.wParam = VK_TAB;
7578     msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7579 
7580     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7581                           ES_MULTILINE|WS_POPUP,
7582                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7583     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7584     msg.hwnd = hwnd;
7585     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7586     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7587     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7588        res, expected);
7589     DestroyWindow(hwnd);
7590 
7591     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7592                           WS_POPUP,
7593                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7594     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7595     msg.hwnd = hwnd;
7596     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7597     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7598     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7599        res, expected);
7600     DestroyWindow(hwnd);
7601 
7602     hold_key(VK_CONTROL);
7603 
7604     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7605                           ES_MULTILINE|WS_POPUP,
7606                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7607     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7608     msg.hwnd = hwnd;
7609     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7610     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7611     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7612        res, expected);
7613     DestroyWindow(hwnd);
7614 
7615     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7616                           WS_POPUP,
7617                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7618     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7619     msg.hwnd = hwnd;
7620     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7621     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7622     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7623        res, expected);
7624     DestroyWindow(hwnd);
7625 
7626     release_key(VK_CONTROL);
7627 
7628     msg.wParam = 'a';
7629     msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7630 
7631     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7632                           ES_MULTILINE|WS_POPUP,
7633                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7634     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7635     msg.hwnd = hwnd;
7636     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7637     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7638     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7639        res, expected);
7640     DestroyWindow(hwnd);
7641 
7642     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7643                           WS_POPUP,
7644                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7645     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7646     msg.hwnd = hwnd;
7647     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7648     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7649     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7650        res, expected);
7651     DestroyWindow(hwnd);
7652 
7653     msg.message = WM_CHAR;
7654 
7655     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7656                           ES_MULTILINE|WS_POPUP,
7657                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7658     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7659     msg.hwnd = hwnd;
7660     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7661     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7662     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7663        res, expected);
7664     DestroyWindow(hwnd);
7665 
7666     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7667                           WS_POPUP,
7668                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7669     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7670     msg.hwnd = hwnd;
7671     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7672     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7673     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7674        res, expected);
7675     DestroyWindow(hwnd);
7676 
7677     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7678                           WS_POPUP|ES_SAVESEL,
7679                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7680     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7681     res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7682     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7683     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7684        res, expected);
7685     DestroyWindow(hwnd);
7686 }
7687 
7688 static void test_zoom(void)
7689 {
7690     HWND hwnd;
7691     UINT ret;
7692     RECT rc;
7693     POINT pt;
7694     int numerator, denominator;
7695 
7696     hwnd = new_richedit(NULL);
7697     GetClientRect(hwnd, &rc);
7698     pt.x = (rc.right - rc.left) / 2;
7699     pt.y = (rc.bottom - rc.top) / 2;
7700     ClientToScreen(hwnd, &pt);
7701 
7702     /* Test initial zoom value */
7703     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7704     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7705     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7706     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7707 
7708     /* test scroll wheel */
7709     hold_key(VK_CONTROL);
7710     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7711                       MAKELPARAM(pt.x, pt.y));
7712     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7713     release_key(VK_CONTROL);
7714 
7715     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7716     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7717     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7718     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7719 
7720     /* Test how much the mouse wheel can zoom in and out. */
7721     ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7722     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7723 
7724     hold_key(VK_CONTROL);
7725     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7726                       MAKELPARAM(pt.x, pt.y));
7727     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7728     release_key(VK_CONTROL);
7729 
7730     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7731     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7732     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7733     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7734 
7735     ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7736     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7737 
7738     hold_key(VK_CONTROL);
7739     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7740                       MAKELPARAM(pt.x, pt.y));
7741     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7742     release_key(VK_CONTROL);
7743 
7744     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7745     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7746     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7747     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7748 
7749     ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7750     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7751 
7752     hold_key(VK_CONTROL);
7753     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7754                       MAKELPARAM(pt.x, pt.y));
7755     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7756     release_key(VK_CONTROL);
7757 
7758     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7759     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7760     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7761     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7762 
7763     ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7764     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7765 
7766     hold_key(VK_CONTROL);
7767     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7768                       MAKELPARAM(pt.x, pt.y));
7769     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7770     release_key(VK_CONTROL);
7771 
7772     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7773     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7774     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7775     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7776 
7777     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7778     ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7779     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7780 
7781     hold_key(VK_CONTROL);
7782     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7783                       MAKELPARAM(pt.x, pt.y));
7784     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7785     release_key(VK_CONTROL);
7786 
7787     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7788     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7789     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7790     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7791 
7792     /* Test bounds checking on EM_SETZOOM */
7793     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7794     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7795 
7796     ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7797     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7798 
7799     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7800     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7801 
7802     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7803     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7804     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7805     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7806 
7807     ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7808     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7809 
7810     /* See if negative numbers are accepted. */
7811     ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7812     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7813 
7814     /* See if negative numbers are accepted. */
7815     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7816     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7817 
7818     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7819     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7820     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7821     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7822 
7823     /* Reset the zoom value */
7824     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7825     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7826 
7827     DestroyWindow(hwnd);
7828 }
7829 
7830 struct dialog_mode_messages
7831 {
7832     int wm_getdefid, wm_close, wm_nextdlgctl;
7833 };
7834 
7835 static struct dialog_mode_messages dm_messages;
7836 
7837 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7838     ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7839     "got %d\n", wmclose, dm_messages.wm_close); \
7840     ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7841     "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7842     ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7843     "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7844 
7845 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7846 {
7847     switch (iMsg)
7848     {
7849         case DM_GETDEFID:
7850             dm_messages.wm_getdefid++;
7851             return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7852         case WM_NEXTDLGCTL:
7853             dm_messages.wm_nextdlgctl++;
7854             break;
7855         case WM_CLOSE:
7856             dm_messages.wm_close++;
7857             break;
7858     }
7859 
7860     return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7861 }
7862 
7863 static void test_dialogmode(void)
7864 {
7865     HWND hwRichEdit, hwParent, hwButton;
7866     MSG msg= {0};
7867     int lcount, r;
7868     WNDCLASSA cls;
7869 
7870     cls.style = 0;
7871     cls.lpfnWndProc = dialog_mode_wnd_proc;
7872     cls.cbClsExtra = 0;
7873     cls.cbWndExtra = 0;
7874     cls.hInstance = GetModuleHandleA(0);
7875     cls.hIcon = 0;
7876     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7877     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7878     cls.lpszMenuName = NULL;
7879     cls.lpszClassName = "DialogModeParentClass";
7880     if(!RegisterClassA(&cls)) assert(0);
7881 
7882     hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7883       CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7884 
7885     /* Test richedit(ES_MULTILINE) */
7886 
7887     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7888 
7889     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7890     ok(0 == r, "expected 0, got %d\n", r);
7891     lcount = SendMessageA(hwRichEdit,  EM_GETLINECOUNT, 0, 0);
7892     ok(2 == lcount, "expected 2, got %d\n", lcount);
7893 
7894     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7895     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7896 
7897     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7898     ok(0 == r, "expected 0, got %d\n", r);
7899     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7900     ok(3 == lcount, "expected 3, got %d\n", lcount);
7901 
7902     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7903     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7904     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7905     ok(0 == r, "expected 0, got %d\n", r);
7906     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7907     ok(3 == lcount, "expected 3, got %d\n", lcount);
7908 
7909     DestroyWindow(hwRichEdit);
7910 
7911     /* Test standalone richedit(ES_MULTILINE) */
7912 
7913     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7914 
7915     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7916     ok(0 == r, "expected 0, got %d\n", r);
7917     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7918     ok(2 == lcount, "expected 2, got %d\n", lcount);
7919 
7920     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7921     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7922 
7923     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7924     ok(0 == r, "expected 0, got %d\n", r);
7925     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7926     ok(2 == lcount, "expected 2, got %d\n", lcount);
7927 
7928     DestroyWindow(hwRichEdit);
7929 
7930     /* Check  a destination for messages */
7931 
7932     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7933 
7934     SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7935     SetParent( hwRichEdit, NULL);
7936 
7937     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7938     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7939 
7940     memset(&dm_messages, 0, sizeof(dm_messages));
7941     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7942     ok(0 == r, "expected 0, got %d\n", r);
7943     test_dm_messages(0, 1, 0);
7944 
7945     memset(&dm_messages, 0, sizeof(dm_messages));
7946     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7947     ok(0 == r, "expected 0, got %d\n", r);
7948     test_dm_messages(0, 0, 1);
7949 
7950     DestroyWindow(hwRichEdit);
7951 
7952     /* Check messages from richedit(ES_MULTILINE) */
7953 
7954     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7955 
7956     memset(&dm_messages, 0, sizeof(dm_messages));
7957     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7958     ok(0 == r, "expected 0, got %d\n", r);
7959     test_dm_messages(0, 0, 0);
7960 
7961     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7962     ok(2 == lcount, "expected 2, got %d\n", lcount);
7963 
7964     memset(&dm_messages, 0, sizeof(dm_messages));
7965     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7966     ok(0 == r, "expected 0, got %d\n", r);
7967     test_dm_messages(0, 0, 0);
7968 
7969     memset(&dm_messages, 0, sizeof(dm_messages));
7970     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7971     ok(0 == r, "expected 0, got %d\n", r);
7972     test_dm_messages(0, 0, 0);
7973 
7974     memset(&dm_messages, 0, sizeof(dm_messages));
7975     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7976     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7977     test_dm_messages(0, 0, 0);
7978 
7979     memset(&dm_messages, 0, sizeof(dm_messages));
7980     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7981     ok(0 == r, "expected 0, got %d\n", r);
7982     test_dm_messages(0, 1, 0);
7983 
7984     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7985     ok(2 == lcount, "expected 2, got %d\n", lcount);
7986 
7987     memset(&dm_messages, 0, sizeof(dm_messages));
7988     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7989     ok(0 == r, "expected 0, got %d\n", r);
7990     test_dm_messages(0, 0, 0);
7991 
7992     memset(&dm_messages, 0, sizeof(dm_messages));
7993     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7994     ok(0 == r, "expected 0, got %d\n", r);
7995     test_dm_messages(0, 0, 1);
7996 
7997     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7998         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7999     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8000 
8001     memset(&dm_messages, 0, sizeof(dm_messages));
8002     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8003     ok(0 == r, "expected 0, got %d\n", r);
8004     test_dm_messages(0, 1, 1);
8005 
8006     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8007     ok(2 == lcount, "expected 2, got %d\n", lcount);
8008 
8009     DestroyWindow(hwButton);
8010     DestroyWindow(hwRichEdit);
8011 
8012     /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
8013 
8014     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
8015 
8016     memset(&dm_messages, 0, sizeof(dm_messages));
8017     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8018     ok(0 == r, "expected 0, got %d\n", r);
8019     test_dm_messages(0, 0, 0);
8020 
8021     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8022     ok(2 == lcount, "expected 2, got %d\n", lcount);
8023 
8024     memset(&dm_messages, 0, sizeof(dm_messages));
8025     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8026     ok(0 == r, "expected 0, got %d\n", r);
8027     test_dm_messages(0, 0, 0);
8028 
8029     memset(&dm_messages, 0, sizeof(dm_messages));
8030     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8031     ok(0 == r, "expected 0, got %d\n", r);
8032     test_dm_messages(0, 0, 0);
8033 
8034     memset(&dm_messages, 0, sizeof(dm_messages));
8035     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8036     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8037     test_dm_messages(0, 0, 0);
8038 
8039     memset(&dm_messages, 0, sizeof(dm_messages));
8040     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8041     ok(0 == r, "expected 0, got %d\n", r);
8042     test_dm_messages(0, 0, 0);
8043 
8044     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8045     ok(3 == lcount, "expected 3, got %d\n", lcount);
8046 
8047     memset(&dm_messages, 0, sizeof(dm_messages));
8048     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8049     ok(0 == r, "expected 0, got %d\n", r);
8050     test_dm_messages(0, 0, 0);
8051 
8052     memset(&dm_messages, 0, sizeof(dm_messages));
8053     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8054     ok(0 == r, "expected 0, got %d\n", r);
8055     test_dm_messages(0, 0, 1);
8056 
8057     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8058         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8059     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8060 
8061     memset(&dm_messages, 0, sizeof(dm_messages));
8062     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8063     ok(0 == r, "expected 0, got %d\n", r);
8064     test_dm_messages(0, 0, 0);
8065 
8066     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8067     ok(4 == lcount, "expected 4, got %d\n", lcount);
8068 
8069     DestroyWindow(hwButton);
8070     DestroyWindow(hwRichEdit);
8071 
8072     /* Check messages from richedit(0) */
8073 
8074     hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
8075 
8076     memset(&dm_messages, 0, sizeof(dm_messages));
8077     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8078     ok(0 == r, "expected 0, got %d\n", r);
8079     test_dm_messages(0, 0, 0);
8080 
8081     memset(&dm_messages, 0, sizeof(dm_messages));
8082     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8083     ok(0 == r, "expected 0, got %d\n", r);
8084     test_dm_messages(0, 0, 0);
8085 
8086     memset(&dm_messages, 0, sizeof(dm_messages));
8087     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8088     ok(0 == r, "expected 0, got %d\n", r);
8089     test_dm_messages(0, 0, 0);
8090 
8091     memset(&dm_messages, 0, sizeof(dm_messages));
8092     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8093     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8094     test_dm_messages(0, 0, 0);
8095 
8096     memset(&dm_messages, 0, sizeof(dm_messages));
8097     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8098     ok(0 == r, "expected 0, got %d\n", r);
8099     test_dm_messages(0, 1, 0);
8100 
8101     memset(&dm_messages, 0, sizeof(dm_messages));
8102     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8103     ok(0 == r, "expected 0, got %d\n", r);
8104     test_dm_messages(0, 0, 0);
8105 
8106     memset(&dm_messages, 0, sizeof(dm_messages));
8107     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8108     ok(0 == r, "expected 0, got %d\n", r);
8109     test_dm_messages(0, 0, 1);
8110 
8111     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8112         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8113     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8114 
8115     memset(&dm_messages, 0, sizeof(dm_messages));
8116     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8117     ok(0 == r, "expected 0, got %d\n", r);
8118     test_dm_messages(0, 1, 1);
8119 
8120     DestroyWindow(hwRichEdit);
8121 
8122     /* Check messages from richedit(ES_WANTRETURN) */
8123 
8124     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8125 
8126     memset(&dm_messages, 0, sizeof(dm_messages));
8127     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8128     ok(0 == r, "expected 0, got %d\n", r);
8129     test_dm_messages(0, 0, 0);
8130 
8131     memset(&dm_messages, 0, sizeof(dm_messages));
8132     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8133     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8134     test_dm_messages(0, 0, 0);
8135 
8136     memset(&dm_messages, 0, sizeof(dm_messages));
8137     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8138     ok(0 == r, "expected 0, got %d\n", r);
8139     test_dm_messages(0, 0, 0);
8140 
8141     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8142         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8143     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8144 
8145     memset(&dm_messages, 0, sizeof(dm_messages));
8146     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8147     ok(0 == r, "expected 0, got %d\n", r);
8148     test_dm_messages(0, 0, 0);
8149 
8150     DestroyWindow(hwRichEdit);
8151     DestroyWindow(hwParent);
8152 }
8153 
8154 static void test_EM_FINDWORDBREAK_W(void)
8155 {
8156     static const struct {
8157         WCHAR c;
8158         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8159     } delimiter_tests[] = {
8160         {0x0a,   FALSE},         /* newline */
8161         {0x0b,   FALSE},         /* vertical tab */
8162         {0x0c,   FALSE},         /* form feed */
8163         {0x0d,   FALSE},         /* carriage return */
8164         {0x20,   TRUE},          /* space */
8165         {0x61,   FALSE},         /* capital letter a */
8166         {0xa0,   FALSE},         /* no-break space */
8167         {0x2000, FALSE},         /* en quad */
8168         {0x3000, FALSE},         /* Ideographic space */
8169         {0x1100, FALSE},         /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8170         {0x11ff, FALSE},         /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8171         {0x115f, FALSE},         /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8172         {0xac00, FALSE},         /* Hangul character GA*/
8173         {0xd7af, FALSE},         /* End of Hangul character chart */
8174         {0xf020, TRUE},          /* MS private for CP_SYMBOL round trip?, see kb897872 */
8175         {0xff20, FALSE},         /* fullwidth commercial @ */
8176         {WCH_EMBEDDING, FALSE},  /* object replacement character*/
8177     };
8178     int i;
8179     HWND hwndRichEdit = new_richeditW(NULL);
8180     ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8181     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8182     {
8183         WCHAR wbuf[2];
8184         int result;
8185 
8186         wbuf[0] = delimiter_tests[i].c;
8187         wbuf[1] = 0;
8188         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8189         result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8190         todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8191             ok(result == delimiter_tests[i].isdelimiter,
8192                 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8193                 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8194     }
8195     DestroyWindow(hwndRichEdit);
8196 }
8197 
8198 static void test_EM_FINDWORDBREAK_A(void)
8199 {
8200     static const struct {
8201         WCHAR c;
8202         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8203     } delimiter_tests[] = {
8204         {0x0a,   FALSE},         /* newline */
8205         {0x0b,   FALSE},         /* vertical tab */
8206         {0x0c,   FALSE},         /* form feed */
8207         {0x0d,   FALSE},         /* carriage return */
8208         {0x20,   TRUE},          /* space */
8209         {0x61,   FALSE},         /* capital letter a */
8210     };
8211     int i;
8212     HWND hwndRichEdit = new_richedit(NULL);
8213 
8214     ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8215     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8216     {
8217         int result;
8218         char buf[2];
8219         buf[0] = delimiter_tests[i].c;
8220         buf[1] = 0;
8221         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8222         result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8223         todo_wine_if (buf[0] == 0x20)
8224             ok(result == delimiter_tests[i].isdelimiter,
8225                "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8226                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8227     }
8228     DestroyWindow(hwndRichEdit);
8229 }
8230 
8231 /*
8232  * This test attempts to show the effect of enter on a richedit
8233  * control v1.0 inserts CRLF whereas for higher versions it only
8234  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8235  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8236  * does for higher. The same test is cloned in riched32 and riched20.
8237  */
8238 static void test_enter(void)
8239 {
8240     static const struct {
8241       const char *initialtext;
8242       const int   cursor;
8243       const char *expectedwmtext;
8244       const char *expectedemtext;
8245       const char *expectedemtextcrlf;
8246     } testenteritems[] = {
8247       { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8248       { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8249       { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8250       { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8251       { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8252     };
8253 
8254   char expectedbuf[1024];
8255   char resultbuf[1024];
8256   HWND hwndRichEdit = new_richedit(NULL);
8257   UINT i,j;
8258 
8259   for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
8260 
8261     char buf[1024] = {0};
8262     LRESULT result;
8263     GETTEXTEX getText;
8264     const char *expected;
8265 
8266     /* Set the text to the initial text */
8267     result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8268     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8269 
8270     /* Send Enter */
8271     SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8272     simulate_typing_characters(hwndRichEdit, "\r");
8273 
8274     /* 1. Retrieve with WM_GETTEXT */
8275     buf[0] = 0x00;
8276     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8277     expected = testenteritems[i].expectedwmtext;
8278 
8279     resultbuf[0]=0x00;
8280     for (j = 0; j < (UINT)result; j++)
8281       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8282     expectedbuf[0] = '\0';
8283     for (j = 0; j < strlen(expected); j++)
8284       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8285 
8286     result = strcmp(expected, buf);
8287     ok (result == 0,
8288         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8289         i, resultbuf, expectedbuf);
8290 
8291     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8292     getText.cb = sizeof(buf);
8293     getText.flags = GT_DEFAULT;
8294     getText.codepage      = CP_ACP;
8295     getText.lpDefaultChar = NULL;
8296     getText.lpUsedDefChar = NULL;
8297     buf[0] = 0x00;
8298     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8299     expected = testenteritems[i].expectedemtext;
8300 
8301     resultbuf[0]=0x00;
8302     for (j = 0; j < (UINT)result; j++)
8303       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8304     expectedbuf[0] = '\0';
8305     for (j = 0; j < strlen(expected); j++)
8306       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8307 
8308     result = strcmp(expected, buf);
8309     ok (result == 0,
8310         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8311         i, resultbuf, expectedbuf);
8312 
8313     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8314     getText.cb = sizeof(buf);
8315     getText.flags = GT_USECRLF;
8316     getText.codepage      = CP_ACP;
8317     getText.lpDefaultChar = NULL;
8318     getText.lpUsedDefChar = NULL;
8319     buf[0] = 0x00;
8320     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8321     expected = testenteritems[i].expectedemtextcrlf;
8322 
8323     resultbuf[0]=0x00;
8324     for (j = 0; j < (UINT)result; j++)
8325       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8326     expectedbuf[0] = '\0';
8327     for (j = 0; j < strlen(expected); j++)
8328       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8329 
8330     result = strcmp(expected, buf);
8331     ok (result == 0,
8332         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8333         i, resultbuf, expectedbuf);
8334   }
8335 
8336   DestroyWindow(hwndRichEdit);
8337 }
8338 
8339 static void test_WM_CREATE(void)
8340 {
8341     static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8342     static const char title[] = "line1\nline2";
8343 
8344     HWND rich_edit;
8345     LRESULT res;
8346     char buf[64];
8347     int len;
8348 
8349     rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8350             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8351     ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8352 
8353     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8354     ok(len == 5, "GetWindowText returned %d\n", len);
8355     ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8356 
8357     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8358     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8359 
8360     DestroyWindow(rich_edit);
8361 
8362     rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8363             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8364     ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8365 
8366     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8367     ok(len == 12, "GetWindowText returned %d\n", len);
8368     ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8369 
8370     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8371     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8372 
8373     DestroyWindow(rich_edit);
8374 }
8375 
8376 /*******************************************************************
8377  * Test that after deleting all of the text, the first paragraph
8378  * format reverts to the default.
8379  */
8380 static void test_reset_default_para_fmt( void )
8381 {
8382     HWND richedit = new_richeditW( NULL );
8383     PARAFORMAT2 fmt;
8384     WORD def_align, new_align;
8385 
8386     memset( &fmt, 0, sizeof(fmt) );
8387     fmt.cbSize = sizeof(PARAFORMAT2);
8388     fmt.dwMask = -1;
8389     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8390     def_align = fmt.wAlignment;
8391     new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8392 
8393     simulate_typing_characters( richedit, "123" );
8394 
8395     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8396     fmt.dwMask = PFM_ALIGNMENT;
8397     fmt.wAlignment = new_align;
8398     SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8399 
8400     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8401     ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8402 
8403     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8404     SendMessageA( richedit, WM_CUT, 0, 0 );
8405 
8406     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8407     ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8408 
8409     DestroyWindow( richedit );
8410 }
8411 
8412 static void test_EM_SETREADONLY(void)
8413 {
8414     HWND richedit = new_richeditW(NULL);
8415     DWORD dwStyle;
8416     LRESULT res;
8417 
8418     res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8419     ok(res == 1, "EM_SETREADONLY\n");
8420     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8421     ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8422 
8423     res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8424     ok(res == 1, "EM_SETREADONLY\n");
8425     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8426     ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8427 
8428     DestroyWindow(richedit);
8429 }
8430 
8431 static inline LONG twips2points(LONG value)
8432 {
8433     return value / 20;
8434 }
8435 
8436 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8437     _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8438 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8439                             LRESULT expected_res, BOOL expected_undo)
8440 {
8441     CHARFORMAT2A cf;
8442     LRESULT res;
8443     BOOL isundo;
8444 
8445     cf.cbSize = sizeof(cf);
8446     cf.dwMask = CFM_SIZE;
8447 
8448     res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8449     SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8450     isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8451     ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8452     ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8453                        twips2points(cf.yHeight), expected_size);
8454     ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8455                        isundo, expected_undo);
8456 }
8457 
8458 static void test_EM_SETFONTSIZE(void)
8459 {
8460     HWND richedit = new_richedit(NULL);
8461     CHAR text[] = "wine";
8462     CHARFORMAT2A tmp_cf;
8463     LONG default_size;
8464 
8465     tmp_cf.cbSize = sizeof(tmp_cf);
8466     tmp_cf.dwMask = CFM_SIZE;
8467     tmp_cf.yHeight = 9 * 20.0;
8468     SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8469 
8470     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8471 
8472     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8473     /* without selection */
8474     TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8475     SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8476     default_size = twips2points(tmp_cf.yHeight);
8477     ok(default_size == 9, "Default font size should not be changed.\n");
8478     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8479 
8480     SendMessageA(richedit, EM_SETSEL, 0, 2);
8481 
8482     TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8483 
8484     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8485     TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8486     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8487 
8488     TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8489     TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8490     TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8491     TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8492     TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8493     TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8494     TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8495     TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8496     TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8497     TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8498     TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8499     TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8500     TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8501     TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8502     TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8503     TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8504     TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8505     TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8506     TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8507     TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8508     TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8509     TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8510     TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8511     TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8512     TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8513     TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8514     TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8515     TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8516 
8517     /* return FALSE when richedit is TM_PLAINTEXT mode */
8518     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8519     SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8520     TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8521 
8522     DestroyWindow(richedit);
8523 }
8524 
8525 static void test_alignment_style(void)
8526 {
8527     HWND richedit = NULL;
8528     PARAFORMAT2 pf;
8529     DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8530                            ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8531                            ES_LEFT | ES_RIGHT | ES_CENTER};
8532     DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8533                           PFA_RIGHT, PFA_CENTER};
8534     const char * streamtext =
8535         "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8536         "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8537         "}\r\n";
8538     EDITSTREAM es;
8539     int i;
8540 
8541     for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8542     {
8543         DWORD dwStyle, new_align;
8544 
8545         richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8546         memset(&pf, 0, sizeof(pf));
8547         pf.cbSize = sizeof(PARAFORMAT2);
8548         pf.dwMask = -1;
8549 
8550         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8551         ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8552            i, pf.wAlignment, align_mask[i]);
8553         dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8554         ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8555            "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8556 
8557 
8558         /* Based on test_reset_default_para_fmt() */
8559         new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8560         simulate_typing_characters(richedit, "123");
8561 
8562         SendMessageW(richedit, EM_SETSEL, 0, -1);
8563         pf.dwMask = PFM_ALIGNMENT;
8564         pf.wAlignment = new_align;
8565         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8566 
8567         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8568         ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8569 
8570         SendMessageW(richedit, EM_SETSEL, 0, -1);
8571         SendMessageW(richedit, WM_CUT, 0, 0);
8572 
8573         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8574         ok(pf.wAlignment == align_mask[i], "got %d expect %d\n", pf.wAlignment, align_mask[i]);
8575 
8576         DestroyWindow(richedit);
8577     }
8578 
8579     /* test with EM_STREAMIN */
8580     richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8581     simulate_typing_characters(richedit, "abc");
8582     es.dwCookie = (DWORD_PTR)&streamtext;
8583     es.dwError = 0;
8584     es.pfnCallback = test_EM_STREAMIN_esCallback;
8585     SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8586     SendMessageW(richedit, EM_SETSEL, 0, -1);
8587     memset(&pf, 0, sizeof(pf));
8588     pf.cbSize = sizeof(PARAFORMAT2);
8589     pf.dwMask = -1;
8590     SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8591     ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8592     DestroyWindow(richedit);
8593 }
8594 
8595 static void test_WM_GETTEXTLENGTH(void)
8596 {
8597     HWND hwndRichEdit = new_richedit(NULL);
8598     static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8599     static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8600     static const char text3[] = "abcdef\x8e\xf0";
8601     int result;
8602 
8603     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8604     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8605     ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8606        result, lstrlenA(text1));
8607 
8608     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8609     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8610     ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8611        result, lstrlenA(text2));
8612 
8613     /* Test with multibyte character */
8614     if (!is_lang_japanese)
8615         skip("Skip multibyte character tests on non-Japanese platform\n");
8616     else
8617     {
8618         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8619         result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8620         todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8621     }
8622 
8623     DestroyWindow(hwndRichEdit);
8624 }
8625 
8626 static void test_rtf(void)
8627 {
8628     const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8629         "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8630     const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8631                                      0x201d,0x200e,0x200f,0x200d,0x200c};
8632     const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8633     const char *highlight = "{\\rtf1{\\colortbl;\\red0\\green0\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;}\\cf2\\highlight3 foo\\par}";
8634 
8635     HWND edit = new_richeditW( NULL );
8636     EDITSTREAM es;
8637     WCHAR buf[80];
8638     LRESULT result;
8639     PARAFORMAT2 fmt;
8640     CHARFORMAT2W cf;
8641 
8642     /* Test rtf specials */
8643     es.dwCookie = (DWORD_PTR)&specials;
8644     es.dwError = 0;
8645     es.pfnCallback = test_EM_STREAMIN_esCallback;
8646     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8647     ok( result == 11, "got %ld\n", result );
8648 
8649     result = SendMessageW( edit, WM_GETTEXT, sizeof(buf)/sizeof(buf[0]), (LPARAM)buf );
8650     ok( result == sizeof(expect_specials)/sizeof(expect_specials[0]), "got %ld\n", result );
8651     ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8652 
8653     /* Show that \rtlpar propagates to the second paragraph and is
8654        reset by \pard in the third. */
8655     es.dwCookie = (DWORD_PTR)&pard;
8656     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8657     ok( result == 11, "got %ld\n", result );
8658 
8659     fmt.cbSize = sizeof(fmt);
8660     SendMessageW( edit, EM_SETSEL, 1, 1 );
8661     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8662     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8663     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8664     SendMessageW( edit, EM_SETSEL, 5, 5 );
8665     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8666     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8667     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8668     SendMessageW( edit, EM_SETSEL, 9, 9 );
8669     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8670     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8671     ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8672 
8673     /* Test \highlight */
8674     es.dwCookie = (DWORD_PTR)&highlight;
8675     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8676     ok( result == 3, "got %ld\n", result );
8677     SendMessageW( edit, EM_SETSEL, 1, 1 );
8678     memset( &cf, 0, sizeof(cf) );
8679     cf.cbSize = sizeof(cf);
8680     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8681     ok( (cf.dwEffects & (CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR)) == 0, "got %08x\n", cf.dwEffects );
8682     ok( cf.crTextColor == RGB(128,128,128), "got %08x\n", cf.crTextColor );
8683     ok( cf.crBackColor == RGB(192,192,192), "got %08x\n", cf.crBackColor );
8684 
8685     DestroyWindow( edit );
8686 }
8687 
8688 static void test_background(void)
8689 {
8690     HWND hwndRichEdit = new_richedit(NULL);
8691 
8692     /* set the background color to black */
8693     ValidateRect(hwndRichEdit, NULL);
8694     SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8695     ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8696 
8697     DestroyWindow(hwndRichEdit);
8698 }
8699 
8700 static void test_eop_char_fmt(void)
8701 {
8702     HWND edit = new_richedit( NULL );
8703     const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8704         "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8705         "\\f0\\fs25 Second\\par"
8706         "{\\f0\\fs26 Third}\\par"
8707         "{\\f0\\fs22 Fourth}\\par}}}";
8708     EDITSTREAM es;
8709     CHARFORMAT2W cf;
8710     int i, num, expect_height;
8711 
8712     es.dwCookie = (DWORD_PTR)&rtf;
8713     es.dwError = 0;
8714     es.pfnCallback = test_EM_STREAMIN_esCallback;
8715     num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8716     ok( num == 25, "got %d\n", num );
8717 
8718     for (i = 0; i <= num; i++)
8719     {
8720         SendMessageW( edit, EM_SETSEL, i, i + 1 );
8721         cf.cbSize = sizeof(cf);
8722         cf.dwMask = CFM_SIZE;
8723         SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8724         ok( cf.dwMask & CFM_SIZE, "%d: got %08x\n", i, cf.dwMask );
8725         if (i < 6) expect_height = 160;
8726         else if (i < 13) expect_height = 250;
8727         else if (i < 18) expect_height = 260;
8728         else if (i == 18 || i == 25) expect_height = 250;
8729         else expect_height = 220;
8730         ok( cf.yHeight == expect_height, "%d: got %d\n", i, cf.yHeight );
8731     }
8732 
8733     DestroyWindow( edit );
8734 }
8735 
8736 static void test_para_numbering(void)
8737 {
8738     HWND edit = new_richeditW( NULL );
8739     const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8740         "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8741         "\\fs20\\fi200\\li360\\f0 First\\par"
8742         "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
8743         "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
8744     const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
8745     EDITSTREAM es;
8746     WCHAR buf[80];
8747     LRESULT result;
8748     PARAFORMAT2 fmt, fmt2;
8749     GETTEXTEX get_text;
8750     CHARFORMAT2W cf;
8751 
8752     get_text.cb = sizeof(buf);
8753     get_text.flags = GT_RAWTEXT;
8754     get_text.codepage = 1200;
8755     get_text.lpDefaultChar = NULL;
8756     get_text.lpUsedDefChar = NULL;
8757 
8758     es.dwCookie = (DWORD_PTR)&numbers;
8759     es.dwError = 0;
8760     es.pfnCallback = test_EM_STREAMIN_esCallback;
8761     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8762     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8763 
8764     result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
8765     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8766     ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
8767 
8768     SendMessageW( edit, EM_SETSEL, 1, 1 );
8769     memset( &fmt, 0, sizeof(fmt) );
8770     fmt.cbSize = sizeof(fmt);
8771     fmt.dwMask = PFM_ALL2;
8772     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8773     ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
8774     ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
8775     ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
8776     ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
8777     ok( fmt.dxStartIndent == 560, "got %d\n", fmt.dxStartIndent );
8778     ok( fmt.dxOffset == -200, "got %d\n", fmt.dxOffset );
8779 
8780     /* Second para should have identical fmt */
8781     SendMessageW( edit, EM_SETSEL, 10, 10 );
8782     memset( &fmt2, 0, sizeof(fmt2) );
8783     fmt2.cbSize = sizeof(fmt2);
8784     fmt2.dwMask = PFM_ALL2;
8785     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
8786     ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
8787 
8788     /* Check the eop heights - this determines the label height */
8789     SendMessageW( edit, EM_SETSEL, 12, 13 );
8790     cf.cbSize = sizeof(cf);
8791     cf.dwMask = CFM_SIZE;
8792     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8793     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8794 
8795     SendMessageW( edit, EM_SETSEL, 18, 19 );
8796     cf.cbSize = sizeof(cf);
8797     cf.dwMask = CFM_SIZE;
8798     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8799     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8800 
8801     DestroyWindow( edit );
8802 }
8803 
8804 static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
8805                                  LPSTORAGE pstg, LPOLECLIENTSITE polesite, LONG sizel_cx,
8806                                  LONG sizel_cy, DWORD aspect, DWORD flags, DWORD user)
8807 {
8808     reobj->cbStruct = sizeof(*reobj);
8809     reobj->clsid = CLSID_NULL;
8810     reobj->cp = cp;
8811     reobj->poleobj = poleobj;
8812     reobj->pstg = pstg;
8813     reobj->polesite = polesite;
8814     reobj->sizel.cx = sizel_cx;
8815     reobj->sizel.cy = sizel_cy;
8816     reobj->dvaspect = aspect;
8817     reobj->dwFlags = flags;
8818     reobj->dwUser = user;
8819 }
8820 
8821 static void test_EM_SELECTIONTYPE(void)
8822 {
8823     HWND hwnd = new_richedit(NULL);
8824     IRichEditOle *reole = NULL;
8825     static const char text1[] = "abcdefg\n";
8826     int result;
8827     REOBJECT reo1, reo2;
8828     IOleClientSite *clientsite;
8829     HRESULT hr;
8830 
8831     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text1);
8832     SendMessageA(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&reole);
8833 
8834     SendMessageA(hwnd, EM_SETSEL, 1, 1);
8835     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8836     ok(result == SEL_EMPTY, "got wrong selection type: %x.\n", result);
8837 
8838     SendMessageA(hwnd, EM_SETSEL, 1, 2);
8839     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8840     ok(result == SEL_TEXT, "got wrong selection type: %x.\n", result);
8841 
8842     SendMessageA(hwnd, EM_SETSEL, 2, 5);
8843     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8844     ok(result == (SEL_TEXT | SEL_MULTICHAR), "got wrong selection type: %x.\n", result);
8845 
8846     SendMessageA(hwnd, EM_SETSEL, 0, 1);
8847     hr = IRichEditOle_GetClientSite(reole, &clientsite);
8848     ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8849     fill_reobject_struct(&reo1, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8850                          DVASPECT_CONTENT, 0, 1);
8851     hr = IRichEditOle_InsertObject(reole, &reo1);
8852     ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8853     IOleClientSite_Release(clientsite);
8854 
8855     SendMessageA(hwnd, EM_SETSEL, 0, 1);
8856     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8857     ok(result == SEL_OBJECT, "got wrong selection type: %x.\n", result);
8858 
8859     SendMessageA(hwnd, EM_SETSEL, 0, 2);
8860     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8861     ok(result == (SEL_TEXT | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8862 
8863     SendMessageA(hwnd, EM_SETSEL, 0, 3);
8864     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8865     ok(result == (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8866 
8867     SendMessageA(hwnd, EM_SETSEL, 2, 3);
8868     hr = IRichEditOle_GetClientSite(reole, &clientsite);
8869     ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8870     fill_reobject_struct(&reo2, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8871                          DVASPECT_CONTENT, 0, 2);
8872     hr = IRichEditOle_InsertObject(reole, &reo2);
8873     ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8874     IOleClientSite_Release(clientsite);
8875 
8876     SendMessageA(hwnd, EM_SETSEL, 0, 2);
8877     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8878     ok(result == (SEL_OBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8879 
8880     SendMessageA(hwnd, EM_SETSEL, 0, 3);
8881     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8882     ok(result == (SEL_OBJECT | SEL_MULTIOBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8883 
8884     SendMessageA(hwnd, EM_SETSEL, 0, 4);
8885     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8886     ok(result == (SEL_TEXT| SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT), "got wrong selection type: %x.\n", result);
8887 
8888     IRichEditOle_Release(reole);
8889     DestroyWindow(hwnd);
8890 }
8891 
8892 static void test_window_classes(void)
8893 {
8894     static const struct
8895     {
8896         const char *class;
8897         BOOL success;
8898     } test[] =
8899     {
8900         { "RichEdit", FALSE },
8901         { "RichEdit20A", TRUE },
8902         { "RichEdit20W", TRUE },
8903         { "RichEdit50A", FALSE },
8904         { "RichEdit50W", FALSE }
8905     };
8906     int i;
8907     HWND hwnd;
8908 
8909     for (i = 0; i < sizeof(test)/sizeof(test[0]); i++)
8910     {
8911         SetLastError(0xdeadbeef);
8912         hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
8913 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
8914         ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
8915            test[i].class, test[i].success ? "succeed" : "fail");
8916         if (!hwnd)
8917 todo_wine
8918             ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %d\n", GetLastError());
8919         else
8920             DestroyWindow(hwnd);
8921     }
8922 }
8923 
8924 START_TEST( editor )
8925 {
8926   BOOL ret;
8927   /* Must explicitly LoadLibrary(). The test has no references to functions in
8928    * RICHED20.DLL, so the linker doesn't actually link to it. */
8929   hmoduleRichEdit = LoadLibraryA("riched20.dll");
8930   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8931   is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
8932 
8933   test_window_classes();
8934   test_WM_CHAR();
8935   test_EM_FINDTEXT(FALSE);
8936   test_EM_FINDTEXT(TRUE);
8937   test_EM_GETLINE();
8938   test_EM_POSFROMCHAR();
8939   test_EM_SCROLLCARET();
8940   test_EM_SCROLL();
8941   test_scrollbar_visibility();
8942   test_WM_SETTEXT();
8943   test_EM_LINELENGTH();
8944   test_EM_SETCHARFORMAT();
8945   test_EM_SETTEXTMODE();
8946   test_TM_PLAINTEXT();
8947   test_EM_SETOPTIONS();
8948   test_WM_GETTEXT();
8949   test_EM_GETTEXTRANGE();
8950   test_EM_GETSELTEXT();
8951   test_EM_SETUNDOLIMIT();
8952   test_ES_PASSWORD();
8953   test_EM_SETTEXTEX();
8954   test_EM_LIMITTEXT();
8955   test_EM_EXLIMITTEXT();
8956   test_EM_GETLIMITTEXT();
8957   test_WM_SETFONT();
8958   test_EM_GETMODIFY();
8959   test_EM_SETSEL();
8960   test_EM_EXSETSEL();
8961   test_WM_PASTE();
8962   test_EM_STREAMIN();
8963   test_EM_STREAMOUT();
8964   test_EM_STREAMOUT_FONTTBL();
8965   test_EM_STREAMOUT_empty_para();
8966   test_EM_StreamIn_Undo();
8967   test_EM_FORMATRANGE();
8968   test_unicode_conversions();
8969   test_EM_GETTEXTLENGTHEX();
8970   test_WM_GETTEXTLENGTH();
8971   test_EM_REPLACESEL(1);
8972   test_EM_REPLACESEL(0);
8973   test_WM_NOTIFY();
8974   test_EN_LINK();
8975   test_EM_AUTOURLDETECT();
8976   test_eventMask();
8977   test_undo_coalescing();
8978   test_word_movement();
8979   test_EM_CHARFROMPOS();
8980   test_SETPARAFORMAT();
8981   test_word_wrap();
8982   test_autoscroll();
8983   test_format_rect();
8984   test_WM_GETDLGCODE();
8985   test_zoom();
8986   test_dialogmode();
8987   test_EM_FINDWORDBREAK_W();
8988   test_EM_FINDWORDBREAK_A();
8989   test_enter();
8990   test_WM_CREATE();
8991   test_reset_default_para_fmt();
8992   test_EM_SETREADONLY();
8993   test_EM_SETFONTSIZE();
8994   test_alignment_style();
8995   test_rtf();
8996   test_background();
8997   test_eop_char_fmt();
8998   test_para_numbering();
8999   test_EM_SELECTIONTYPE();
9000 
9001   /* Set the environment variable WINETEST_RICHED20 to keep windows
9002    * responsive and open for 30 seconds. This is useful for debugging.
9003    */
9004   if (getenv( "WINETEST_RICHED20" )) {
9005     keep_responsive(30);
9006   }
9007 
9008   OleFlushClipboard();
9009   ret = FreeLibrary(hmoduleRichEdit);
9010   ok(ret, "error: %d\n", (int) GetLastError());
9011 }
9012