1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22 
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <commdlg.h>
34 #include <time.h>
35 #include <wine/test.h>
36 
37 #define ID_RICHEDITTESTDBUTTON 0x123
38 
39 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 
41 #define ok_w3(format, szString1, szString2, szString3) \
42     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
43     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
44     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
45     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
46        format, string1, string2, string3);
47 
48 static HMODULE hmoduleRichEdit;
49 static BOOL is_lang_japanese;
50 
51 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
52   HWND hwnd;
53   hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55                       hmoduleRichEdit, NULL);
56   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
57   return hwnd;
58 }
59 
60 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
61   HWND hwnd;
62   hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
63                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
64                       hmoduleRichEdit, NULL);
65   ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
66   return hwnd;
67 }
68 
69 static HWND new_richedit(HWND parent) {
70   return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
71 }
72 
73 static HWND new_richedit_with_style(HWND parent, DWORD style) {
74   return new_window(RICHEDIT_CLASS20A, style, parent);
75 }
76 
77 static HWND new_richeditW(HWND parent) {
78   return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
79 }
80 
81 /* Keeps the window reponsive for the deley_time in seconds.
82  * This is useful for debugging a test to see what is happening. */
83 static void keep_responsive(time_t delay_time)
84 {
85     MSG msg;
86     time_t end;
87 
88     /* The message pump uses PeekMessage() to empty the queue and then
89      * sleeps for 50ms before retrying the queue. */
90     end = time(NULL) + delay_time;
91     while (time(NULL) < end) {
92       if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
93         TranslateMessage(&msg);
94         DispatchMessageA(&msg);
95       } else {
96         Sleep(50);
97       }
98     }
99 }
100 
101 static void simulate_typing_characters(HWND hwnd, const char* szChars)
102 {
103     int ret;
104 
105     while (*szChars != '\0') {
106         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
107         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
108         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
109         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
110         szChars++;
111     }
112 }
113 
114 static BOOL hold_key(int vk)
115 {
116   BYTE key_state[256];
117   BOOL result;
118 
119   result = GetKeyboardState(key_state);
120   ok(result, "GetKeyboardState failed.\n");
121   if (!result) return FALSE;
122   key_state[vk] |= 0x80;
123   result = SetKeyboardState(key_state);
124   ok(result, "SetKeyboardState failed.\n");
125   return result != 0;
126 }
127 
128 static BOOL release_key(int vk)
129 {
130   BYTE key_state[256];
131   BOOL result;
132 
133   result = GetKeyboardState(key_state);
134   ok(result, "GetKeyboardState failed.\n");
135   if (!result) return FALSE;
136   key_state[vk] &= ~0x80;
137   result = SetKeyboardState(key_state);
138   ok(result, "SetKeyboardState failed.\n");
139   return result != 0;
140 }
141 
142 static const char haystack[] = "WINEWine wineWine wine WineWine";
143                              /* ^0        ^10       ^20       ^30 */
144 
145 struct find_s {
146   int start;
147   int end;
148   const char *needle;
149   int flags;
150   int expected_loc;
151 };
152 
153 
154 static struct find_s find_tests[] = {
155   /* Find in empty text */
156   {0, -1, "foo", FR_DOWN, -1},
157   {0, -1, "foo", 0, -1},
158   {0, -1, "", FR_DOWN, -1},
159   {20, 5, "foo", FR_DOWN, -1},
160   {5, 20, "foo", FR_DOWN, -1}
161 };
162 
163 static struct find_s find_tests2[] = {
164   /* No-result find */
165   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
166   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
167 
168   /* Subsequent finds */
169   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
170   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
171   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
172   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
173 
174   /* Find backwards */
175   {19, 20, "Wine", FR_MATCHCASE, 13},
176   {10, 20, "Wine", FR_MATCHCASE, 4},
177   {20, 10, "Wine", FR_MATCHCASE, 13},
178 
179   /* Case-insensitive */
180   {1, 31, "wInE", FR_DOWN, 4},
181   {1, 31, "Wine", FR_DOWN, 4},
182 
183   /* High-to-low ranges */
184   {20, 5, "Wine", FR_DOWN, -1},
185   {2, 1, "Wine", FR_DOWN, -1},
186   {30, 29, "Wine", FR_DOWN, -1},
187   {20, 5, "Wine", 0, 13},
188 
189   /* Find nothing */
190   {5, 10, "", FR_DOWN, -1},
191   {10, 5, "", FR_DOWN, -1},
192   {0, -1, "", FR_DOWN, -1},
193   {10, 5, "", 0, -1},
194 
195   /* Whole-word search */
196   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
197   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
198   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
199   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
200   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
201   {11, -1, "winewine", FR_WHOLEWORD, 0},
202   {31, -1, "winewine", FR_WHOLEWORD, 23},
203 
204   /* Bad ranges */
205   {5, 200, "XXX", FR_DOWN, -1},
206   {-20, 20, "Wine", FR_DOWN, -1},
207   {-20, 20, "Wine", FR_DOWN, -1},
208   {-15, -20, "Wine", FR_DOWN, -1},
209   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
210 
211   /* Check the case noted in bug 4479 where matches at end aren't recognized */
212   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
213   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
214   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
215   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
216   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
217 
218   /* The backwards case of bug 4479; bounds look right
219    * Fails because backward find is wrong */
220   {19, 20, "WINE", FR_MATCHCASE, 0},
221   {0, 20, "WINE", FR_MATCHCASE, -1},
222 
223   {0, -1, "wineWine wine", 0, -1},
224 };
225 
226 static WCHAR *atowstr(const char *str)
227 {
228     WCHAR *ret;
229     DWORD len;
230     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
231     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
232     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
233     return ret;
234 }
235 
236 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
237 {
238   int findloc;
239 
240   if(unicode){
241       FINDTEXTW ftw;
242       memset(&ftw, 0, sizeof(ftw));
243       ftw.chrg.cpMin = f->start;
244       ftw.chrg.cpMax = f->end;
245       ftw.lpstrText = atowstr(f->needle);
246 
247       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
248       ok(findloc == f->expected_loc,
249          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
250          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
251 
252       findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
253       ok(findloc == f->expected_loc,
254          "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
255          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
256 
257       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
258   }else{
259       FINDTEXTA fta;
260       memset(&fta, 0, sizeof(fta));
261       fta.chrg.cpMin = f->start;
262       fta.chrg.cpMax = f->end;
263       fta.lpstrText = f->needle;
264 
265       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
266       ok(findloc == f->expected_loc,
267          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
268          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
269   }
270 }
271 
272 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
273     int id, BOOL unicode)
274 {
275   int findloc;
276   int expected_end_loc;
277 
278   if(unicode){
279       FINDTEXTEXW ftw;
280       memset(&ftw, 0, sizeof(ftw));
281       ftw.chrg.cpMin = f->start;
282       ftw.chrg.cpMax = f->end;
283       ftw.lpstrText = atowstr(f->needle);
284       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
285       ok(findloc == f->expected_loc,
286           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
287           name, id, f->needle, f->start, f->end, f->flags, findloc);
288       ok(ftw.chrgText.cpMin == 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, ftw.chrgText.cpMin);
291       expected_end_loc = ((f->expected_loc == -1) ? -1
292             : f->expected_loc + strlen(f->needle));
293       ok(ftw.chrgText.cpMax == expected_end_loc,
294           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
295           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
296       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
297   }else{
298       FINDTEXTEXA fta;
299       memset(&fta, 0, sizeof(fta));
300       fta.chrg.cpMin = f->start;
301       fta.chrg.cpMax = f->end;
302       fta.lpstrText = f->needle;
303       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
304       ok(findloc == f->expected_loc,
305           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
306           name, id, f->needle, f->start, f->end, f->flags, findloc);
307       ok(fta.chrgText.cpMin == 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, fta.chrgText.cpMin);
310       expected_end_loc = ((f->expected_loc == -1) ? -1
311             : f->expected_loc + strlen(f->needle));
312       ok(fta.chrgText.cpMax == expected_end_loc,
313           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
314           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
315   }
316 }
317 
318 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
319     int num_tests, BOOL unicode)
320 {
321   int i;
322 
323   for (i = 0; i < num_tests; i++) {
324       check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
325       check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
326   }
327 }
328 
329 static void test_EM_FINDTEXT(BOOL unicode)
330 {
331   HWND hwndRichEdit;
332   CHARFORMAT2A cf2;
333 
334   if(unicode)
335        hwndRichEdit = new_richeditW(NULL);
336   else
337        hwndRichEdit = new_richedit(NULL);
338 
339   /* Empty rich edit control */
340   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
341       sizeof(find_tests)/sizeof(struct find_s), unicode);
342 
343   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
344 
345   /* Haystack text */
346   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
347       sizeof(find_tests2)/sizeof(struct find_s), unicode);
348 
349   /* Setting a format on an arbitrary range should have no effect in search
350      results. This tests correct offset reporting across runs. */
351   cf2.cbSize = sizeof(CHARFORMAT2A);
352   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
353   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
354   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
355   SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
356   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
357 
358   /* Haystack text, again */
359   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
360       sizeof(find_tests2)/sizeof(struct find_s), unicode);
361 
362   /* Yet another range */
363   cf2.dwMask = CFM_BOLD | cf2.dwMask;
364   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
365   SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
366   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
367 
368   /* Haystack text, again */
369   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
370       sizeof(find_tests2)/sizeof(struct find_s), unicode);
371 
372   DestroyWindow(hwndRichEdit);
373 }
374 
375 static const struct getline_s {
376   int line;
377   size_t buffer_len;
378   const char *text;
379 } gl[] = {
380   {0, 10, "foo bar\r"},
381   {1, 10, "\r"},
382   {2, 10, "bar\r"},
383   {3, 10, "\r"},
384 
385   /* Buffer smaller than line length */
386   {0, 2, "foo bar\r"},
387   {0, 1, "foo bar\r"},
388   {0, 0, "foo bar\r"}
389 };
390 
391 static void test_EM_GETLINE(void)
392 {
393   int i;
394   HWND hwndRichEdit = new_richedit(NULL);
395   static const int nBuf = 1024;
396   char dest[1024], origdest[1024];
397   const char text[] = "foo bar\n"
398       "\n"
399       "bar\n";
400 
401   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
402 
403   memset(origdest, 0xBB, nBuf);
404   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
405   {
406     int nCopied;
407     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
408     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
409     memset(dest, 0xBB, nBuf);
410     *(WORD *) dest = gl[i].buffer_len;
411 
412     /* EM_GETLINE appends a "\r\0" to the end of the line
413      * nCopied counts up to and including the '\r' */
414     nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
415     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
416        expected_nCopied);
417     /* two special cases since a parameter is passed via dest */
418     if (gl[i].buffer_len == 0)
419       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
420          "buffer_len=0\n");
421     else if (gl[i].buffer_len == 1)
422       ok(dest[0] == gl[i].text[0] && !dest[1] &&
423          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
424     else
425     {
426       /* Prepare hex strings of buffers to dump on failure. */
427       char expectedbuf[1024];
428       char resultbuf[1024];
429       int j;
430       resultbuf[0] = '\0';
431       for (j = 0; j < 32; j++)
432         sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
433       expectedbuf[0] = '\0';
434       for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
435         sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
436       for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
437         sprintf(expectedbuf+strlen(expectedbuf), "??");
438       for (; j < 32; j++) /* Bytes after declared buffer size */
439         sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
440 
441       /* Test the part of the buffer that is expected to be written according
442        * to the MSDN documentation fo EM_GETLINE, which does not state that
443        * a NULL terminating character will be added unless no text is copied.
444        *
445        * Windows NT does not append a NULL terminating character, but
446        * Windows 2000 and up do append a NULL terminating character if there
447        * is space in the buffer. The test will ignore this difference. */
448       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
449          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
450          i, expected_bytes_written, expectedbuf, resultbuf);
451       /* Test the part of the buffer after the declared length to make sure
452        * there are no buffer overruns. */
453       ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
454                   nBuf - gl[i].buffer_len),
455          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
456          i, expected_bytes_written, expectedbuf, resultbuf);
457     }
458   }
459 
460   DestroyWindow(hwndRichEdit);
461 }
462 
463 static void test_EM_LINELENGTH(void)
464 {
465   HWND hwndRichEdit = new_richedit(NULL);
466   const char * text =
467         "richedit1\r"
468         "richedit1\n"
469         "richedit1\r\n"
470         "richedit1";
471   int offset_test[10][2] = {
472         {0, 9},
473         {5, 9},
474         {10, 9},
475         {15, 9},
476         {20, 9},
477         {25, 9},
478         {30, 9},
479         {35, 9},
480         {40, 0},
481         {45, 0},
482   };
483   int i;
484   LRESULT result;
485 
486   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
487 
488   for (i = 0; i < 10; i++) {
489     result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
490     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
491         offset_test[i][0], result, offset_test[i][1]);
492   }
493 
494   /* Test with multibyte character */
495   if (!is_lang_japanese)
496     skip("Skip multibyte character tests on non-Japanese platform\n");
497   else
498   {
499     const char *text1 =
500           "wine\n"
501           "richedit\x8e\xf0\n"
502           "wine";
503     int offset_test1[3][2] = {
504            {0, 4},  /* Line 1: |wine\n */
505            {5, 9},  /* Line 2: |richedit\x8e\xf0\n */
506            {15, 4}, /* Line 3: |wine */
507     };
508     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
509     for (i = 0; i < sizeof(offset_test1)/sizeof(offset_test1[0]); i++) {
510       result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
511       ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
512          offset_test1[i][0], result, offset_test1[i][1]);
513     }
514   }
515 
516   DestroyWindow(hwndRichEdit);
517 }
518 
519 static int get_scroll_pos_y(HWND hwnd)
520 {
521   POINT p = {-1, -1};
522   SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
523   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
524   return p.y;
525 }
526 
527 static void move_cursor(HWND hwnd, LONG charindex)
528 {
529   CHARRANGE cr;
530   cr.cpMax = charindex;
531   cr.cpMin = charindex;
532   SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
533 }
534 
535 static void line_scroll(HWND hwnd, int amount)
536 {
537   SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
538 }
539 
540 static void test_EM_SCROLLCARET(void)
541 {
542   int prevY, curY;
543   const char text[] = "aa\n"
544       "this is a long line of text that should be longer than the "
545       "control's width\n"
546       "cc\n"
547       "dd\n"
548       "ee\n"
549       "ff\n"
550       "gg\n"
551       "hh\n";
552   /* The richedit window height needs to be large enough vertically to fit in
553    * more than two lines of text, so the new_richedit function can't be used
554    * since a height of 60 was not large enough on some systems.
555    */
556   HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
557                                    ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
558                                    0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
559   ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
560 
561   /* Can't verify this */
562   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
563 
564   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
565 
566   /* Caret above visible window */
567   line_scroll(hwndRichEdit, 3);
568   prevY = get_scroll_pos_y(hwndRichEdit);
569   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
570   curY = get_scroll_pos_y(hwndRichEdit);
571   ok(prevY != curY, "%d == %d\n", prevY, curY);
572 
573   /* Caret below visible window */
574   move_cursor(hwndRichEdit, sizeof(text) - 1);
575   line_scroll(hwndRichEdit, -3);
576   prevY = get_scroll_pos_y(hwndRichEdit);
577   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
578   curY = get_scroll_pos_y(hwndRichEdit);
579   ok(prevY != curY, "%d == %d\n", prevY, curY);
580 
581   /* Caret in visible window */
582   move_cursor(hwndRichEdit, sizeof(text) - 2);
583   prevY = get_scroll_pos_y(hwndRichEdit);
584   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
585   curY = get_scroll_pos_y(hwndRichEdit);
586   ok(prevY == curY, "%d != %d\n", prevY, curY);
587 
588   /* Caret still in visible window */
589   line_scroll(hwndRichEdit, -1);
590   prevY = get_scroll_pos_y(hwndRichEdit);
591   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
592   curY = get_scroll_pos_y(hwndRichEdit);
593   ok(prevY == curY, "%d != %d\n", prevY, curY);
594 
595   DestroyWindow(hwndRichEdit);
596 }
597 
598 static void test_EM_POSFROMCHAR(void)
599 {
600   HWND hwndRichEdit = new_richedit(NULL);
601   int i, expected;
602   LRESULT result;
603   unsigned int height = 0;
604   int xpos = 0;
605   POINTL pt;
606   LOCALESIGNATURE sig;
607   BOOL rtl;
608   PARAFORMAT2 fmt;
609   static const char text[] = "aa\n"
610       "this is a long line of text that should be longer than the "
611       "control's width\n"
612       "cc\n"
613       "dd\n"
614       "ee\n"
615       "ff\n"
616       "gg\n"
617       "hh\n";
618 
619   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
620                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
621          (sig.lsUsb[3] & 0x08000000) != 0);
622 
623   /* Fill the control to lines to ensure that most of them are offscreen */
624   for (i = 0; i < 50; i++)
625   {
626     /* Do not modify the string; it is exactly 16 characters long. */
627     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
628     SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
629   }
630 
631   /*
632    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
633    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
634    Richedit 3.0 accepts either of the above API conventions.
635    */
636 
637   /* Testing Richedit 2.0 API format */
638 
639   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
640      Since all lines are identical and drawn with the same font,
641      they should have the same height... right?
642    */
643   for (i = 0; i < 50; i++)
644   {
645     /* All the lines are 16 characters long */
646     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
647     if (i == 0)
648     {
649       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
650       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
651       xpos = LOWORD(result);
652     }
653     else if (i == 1)
654     {
655       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
656       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
657       height = HIWORD(result);
658     }
659     else
660     {
661       ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
662       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
663     }
664   }
665 
666   /* Testing position at end of text */
667   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
668   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
669   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
670 
671   /* Testing position way past end of text */
672   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
673   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
674   expected = (rtl ? 8 : 1);
675   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
676 
677   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
678   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
679   for (i = 0; i < 50; i++)
680   {
681     /* All the lines are 16 characters long */
682     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
683     ok((signed short)(HIWORD(result)) == (i - 1) * height,
684         "EM_POSFROMCHAR reports y=%hd, expected %d\n",
685         (signed short)(HIWORD(result)), (i - 1) * height);
686     ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
687   }
688 
689   /* Testing position at end of text */
690   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
691   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
692   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
693 
694   /* Testing position way past end of text */
695   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
696   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
697   expected = (rtl ? 8 : 1);
698   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
699 
700   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
701   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
702   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
703 
704   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
705   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
706   ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
707   xpos = LOWORD(result);
708 
709   SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
710   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
711   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
712   ok((signed short)(LOWORD(result)) < xpos,
713         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
714         (signed short)(LOWORD(result)), xpos);
715   SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
716 
717   /* Test around end of text that doesn't end in a newline. */
718   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
719   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
721   ok(pt.x > 1, "pt.x = %d\n", pt.x);
722   xpos = pt.x;
723   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
725   ok(pt.x > xpos, "pt.x = %d\n", pt.x);
726   xpos = (rtl ? pt.x + 7 : pt.x);
727   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
728               SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
729   ok(pt.x == xpos, "pt.x = %d\n", pt.x);
730 
731   /* Try a negative position. */
732   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
733   ok(pt.x == 1, "pt.x = %d\n", pt.x);
734 
735   /* test negative indentation */
736   SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
737           (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
738   SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
739   ok(pt.x == 1, "pt.x = %d\n", pt.x);
740 
741   fmt.cbSize = sizeof(fmt);
742   SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
743   ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
744   ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
745   ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
746 
747   DestroyWindow(hwndRichEdit);
748 }
749 
750 static void test_EM_SETCHARFORMAT(void)
751 {
752   HWND hwndRichEdit = new_richedit(NULL);
753   CHARFORMAT2A cf2;
754   CHARFORMAT2W cfW;
755   int rc = 0;
756   int tested_effects[] = {
757     CFE_BOLD,
758     CFE_ITALIC,
759     CFE_UNDERLINE,
760     CFE_STRIKEOUT,
761     CFE_PROTECTED,
762     CFE_LINK,
763     CFE_SUBSCRIPT,
764     CFE_SUPERSCRIPT,
765     0
766   };
767   int i;
768   CHARRANGE cr;
769   LOCALESIGNATURE sig;
770   BOOL rtl;
771   DWORD expect_effects;
772 
773   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
774                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
775          (sig.lsUsb[3] & 0x08000000) != 0);
776 
777   /* check charformat defaults */
778   memset(&cf2, 0, sizeof(CHARFORMAT2A));
779   cf2.cbSize = sizeof(CHARFORMAT2A);
780   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
781   ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
782   expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
783   if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
784   ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
785   ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
786   ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
787   ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
788   ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
789   ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
790   ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
791   ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
792 
793   /* Invalid flags, CHARFORMAT2 structure blanked out */
794   memset(&cf2, 0, sizeof(cf2));
795   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
796   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
797 
798   /* A valid flag, CHARFORMAT2 structure blanked out */
799   memset(&cf2, 0, sizeof(cf2));
800   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
801   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
802 
803   /* A valid flag, CHARFORMAT2 structure blanked out */
804   memset(&cf2, 0, sizeof(cf2));
805   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
806   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
807 
808   /* A valid flag, CHARFORMAT2 structure blanked out */
809   memset(&cf2, 0, sizeof(cf2));
810   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
811   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
812 
813   /* A valid flag, CHARFORMAT2 structure blanked out */
814   memset(&cf2, 0, sizeof(cf2));
815   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
816   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
817 
818   /* Invalid flags, CHARFORMAT2 structure minimally filled */
819   memset(&cf2, 0, sizeof(cf2));
820   cf2.cbSize = sizeof(CHARFORMAT2A);
821   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
822   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
823   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
824   ok(rc == FALSE, "Should not be able to undo here.\n");
825   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
826 
827   /* A valid flag, CHARFORMAT2 structure minimally filled */
828   memset(&cf2, 0, sizeof(cf2));
829   cf2.cbSize = sizeof(CHARFORMAT2A);
830   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
831   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
832   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
833   ok(rc == FALSE, "Should not be able to undo here.\n");
834   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
835 
836   /* A valid flag, CHARFORMAT2 structure minimally filled */
837   memset(&cf2, 0, sizeof(cf2));
838   cf2.cbSize = sizeof(CHARFORMAT2A);
839   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
840   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
841   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
842   ok(rc == FALSE, "Should not be able to undo here.\n");
843   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
844 
845   /* A valid flag, CHARFORMAT2 structure minimally filled */
846   memset(&cf2, 0, sizeof(cf2));
847   cf2.cbSize = sizeof(CHARFORMAT2A);
848   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
849   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
850   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
851   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
852   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
853 
854   /* A valid flag, CHARFORMAT2 structure minimally filled */
855   memset(&cf2, 0, sizeof(cf2));
856   cf2.cbSize = sizeof(CHARFORMAT2A);
857   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
858   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
859   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
860   ok(rc == TRUE, "Should not be able to undo here.\n");
861   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
862 
863   cf2.cbSize = sizeof(CHARFORMAT2A);
864   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
865 
866   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
867   cf2.cbSize = sizeof(CHARFORMAT2A);
868   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
869   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
870   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
871 
872   /* wParam==0 is default char format, does not set modify */
873   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
874   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
875   ok(rc == 0, "Text marked as modified, expected not modified!\n");
876   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
877   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
878   if (! rtl)
879   {
880     rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
881     ok(rc == 0, "Text marked as modified, expected not modified!\n");
882   }
883   else
884     skip("RTL language found\n");
885 
886   /* wParam==SCF_SELECTION sets modify if nonempty selection */
887   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
888   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
889   ok(rc == 0, "Text marked as modified, expected not modified!\n");
890   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
891   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
892   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
893   ok(rc == 0, "Text marked as modified, expected not modified!\n");
894 
895   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
896   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
897   ok(rc == 0, "Text marked as modified, expected not modified!\n");
898   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
899   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
900   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
901   ok(rc == 0, "Text marked as modified, expected not modified!\n");
902   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
903   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
904   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
905   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
906   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
907 
908   /* wParam==SCF_ALL sets modify regardless of whether text is present */
909   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
910   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
911   ok(rc == 0, "Text marked as modified, expected not modified!\n");
912   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
913   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
914   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
915   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
916 
917   DestroyWindow(hwndRichEdit);
918 
919   /* EM_GETCHARFORMAT tests */
920   for (i = 0; tested_effects[i]; i++)
921   {
922     hwndRichEdit = new_richedit(NULL);
923     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
924 
925     /* Need to set a TrueType font to get consistent CFM_BOLD results */
926     memset(&cf2, 0, sizeof(CHARFORMAT2A));
927     cf2.cbSize = sizeof(CHARFORMAT2A);
928     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
929     cf2.dwEffects = 0;
930     strcpy(cf2.szFaceName, "Courier New");
931     cf2.wWeight = FW_DONTCARE;
932     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
933 
934     memset(&cf2, 0, sizeof(CHARFORMAT2A));
935     cf2.cbSize = sizeof(CHARFORMAT2A);
936     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
937     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
938     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
939           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
940           ||
941           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
942         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
943     ok((cf2.dwEffects & tested_effects[i]) == 0,
944         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
945 
946     memset(&cf2, 0, sizeof(CHARFORMAT2A));
947     cf2.cbSize = sizeof(CHARFORMAT2A);
948     cf2.dwMask = tested_effects[i];
949     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
950       cf2.dwMask = CFM_SUPERSCRIPT;
951     cf2.dwEffects = tested_effects[i];
952     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
953     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
954 
955     memset(&cf2, 0, sizeof(CHARFORMAT2A));
956     cf2.cbSize = sizeof(CHARFORMAT2A);
957     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
958     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
959     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
960           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
961           ||
962           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
963         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
964     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
965         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
966 
967     memset(&cf2, 0, sizeof(CHARFORMAT2A));
968     cf2.cbSize = sizeof(CHARFORMAT2A);
969     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
970     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
971     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
972           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
973           ||
974           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
975         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
976     ok((cf2.dwEffects & tested_effects[i]) == 0,
977         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
978 
979     memset(&cf2, 0, sizeof(CHARFORMAT2A));
980     cf2.cbSize = sizeof(CHARFORMAT2A);
981     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
982     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
983     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
984           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
985           ||
986           (cf2.dwMask & tested_effects[i]) == 0),
987         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
988 
989     DestroyWindow(hwndRichEdit);
990   }
991 
992   for (i = 0; tested_effects[i]; i++)
993   {
994     hwndRichEdit = new_richedit(NULL);
995     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
996 
997     /* Need to set a TrueType font to get consistent CFM_BOLD results */
998     memset(&cf2, 0, sizeof(CHARFORMAT2A));
999     cf2.cbSize = sizeof(CHARFORMAT2A);
1000     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1001     cf2.dwEffects = 0;
1002     strcpy(cf2.szFaceName, "Courier New");
1003     cf2.wWeight = FW_DONTCARE;
1004     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1005 
1006     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1007     cf2.cbSize = sizeof(CHARFORMAT2A);
1008     cf2.dwMask = tested_effects[i];
1009     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1010       cf2.dwMask = CFM_SUPERSCRIPT;
1011     cf2.dwEffects = tested_effects[i];
1012     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1013     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1014 
1015     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1016     cf2.cbSize = sizeof(CHARFORMAT2A);
1017     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1018     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1019     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1020           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1021           ||
1022           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1023         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1024     ok((cf2.dwEffects & tested_effects[i]) == 0,
1025         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1026 
1027     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1028     cf2.cbSize = sizeof(CHARFORMAT2A);
1029     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1030     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1031     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1032           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1033           ||
1034           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1035         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1036     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1037         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1038 
1039     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1040     cf2.cbSize = sizeof(CHARFORMAT2A);
1041     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1042     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1043     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1044           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1045           ||
1046           (cf2.dwMask & tested_effects[i]) == 0),
1047         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1048     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1049         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1050 
1051     DestroyWindow(hwndRichEdit);
1052   }
1053 
1054   /* Effects applied on an empty selection should take effect when selection is
1055      replaced with text */
1056   hwndRichEdit = new_richedit(NULL);
1057   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1058   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1059 
1060   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1061   cf2.cbSize = sizeof(CHARFORMAT2A);
1062   cf2.dwMask = CFM_BOLD;
1063   cf2.dwEffects = CFE_BOLD;
1064   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1065 
1066   /* Selection is now nonempty */
1067   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1068 
1069   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1070   cf2.cbSize = sizeof(CHARFORMAT2A);
1071   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1072   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1073 
1074   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1075       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1076   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1077       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1078 
1079 
1080   /* Set two effects on an empty selection */
1081   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1082   /* first clear bold, italic */
1083   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1084   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1085   cf2.cbSize = sizeof(CHARFORMAT2A);
1086   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1087   cf2.dwEffects = 0;
1088   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1089 
1090   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1091 
1092   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1093   cf2.cbSize = sizeof(CHARFORMAT2A);
1094   cf2.dwMask = CFM_BOLD;
1095   cf2.dwEffects = CFE_BOLD;
1096   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1097   cf2.dwMask = CFM_ITALIC;
1098   cf2.dwEffects = CFE_ITALIC;
1099   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1100 
1101   /* Selection is now nonempty */
1102   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1103 
1104   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1105   cf2.cbSize = sizeof(CHARFORMAT2A);
1106   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1107   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1108 
1109   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1110       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1111   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1112       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1113 
1114   /* Setting the (empty) selection to exactly the same place as before should
1115      NOT clear the insertion style! */
1116   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1117   /* first clear bold, italic */
1118   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1119   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1120   cf2.cbSize = sizeof(CHARFORMAT2A);
1121   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1122   cf2.dwEffects = 0;
1123   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1124 
1125   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1126 
1127   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1128   cf2.cbSize = sizeof(CHARFORMAT2A);
1129   cf2.dwMask = CFM_BOLD;
1130   cf2.dwEffects = CFE_BOLD;
1131   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1132 
1133   /* Empty selection in same place, insert style should NOT be forgotten here. */
1134   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1135 
1136   /* Selection is now nonempty */
1137   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1138 
1139   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1140   cf2.cbSize = sizeof(CHARFORMAT2A);
1141   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1142   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1143 
1144   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1145       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1146   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1147       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1148 
1149   /* Moving the selection will clear the insertion style */
1150   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1151   /* first clear bold, italic */
1152   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1153   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1154   cf2.cbSize = sizeof(CHARFORMAT2A);
1155   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1156   cf2.dwEffects = 0;
1157   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1158 
1159   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1160 
1161   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1162   cf2.cbSize = sizeof(CHARFORMAT2A);
1163   cf2.dwMask = CFM_BOLD;
1164   cf2.dwEffects = CFE_BOLD;
1165   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1166 
1167   /* Move selection and then put it back, insert style should be forgotten here. */
1168   SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1169   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1170 
1171   /* Selection is now nonempty */
1172   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1173 
1174   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1175   cf2.cbSize = sizeof(CHARFORMAT2A);
1176   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1177   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1178 
1179   ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1180       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1181   ok((cf2.dwEffects & CFE_BOLD) == 0,
1182       "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1183 
1184   /* Ditto with EM_EXSETSEL */
1185   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1186   /* first clear bold, italic */
1187   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1188   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1189   cf2.cbSize = sizeof(CHARFORMAT2A);
1190   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1191   cf2.dwEffects = 0;
1192   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1193 
1194   cr.cpMin = 2; cr.cpMax = 2;
1195   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1196 
1197   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1198   cf2.cbSize = sizeof(CHARFORMAT2A);
1199   cf2.dwMask = CFM_BOLD;
1200   cf2.dwEffects = CFE_BOLD;
1201   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1202 
1203   /* Empty selection in same place, insert style should NOT be forgotten here. */
1204   cr.cpMin = 2; cr.cpMax = 2;
1205   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1206 
1207   /* Selection is now nonempty */
1208   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1209 
1210   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1211   cf2.cbSize = sizeof(CHARFORMAT2A);
1212   cr.cpMin = 2; cr.cpMax = 6;
1213   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1214   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1215 
1216   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1217       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1218   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1219       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1220 
1221   /* show that wWeight is at the correct offset in CHARFORMAT2A */
1222   memset(&cf2, 0, sizeof(cf2));
1223   cf2.cbSize = sizeof(cf2);
1224   cf2.dwMask = CFM_WEIGHT;
1225   cf2.wWeight = 100;
1226   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1227   memset(&cf2, 0, sizeof(cf2));
1228   cf2.cbSize = sizeof(cf2);
1229   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1230   ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1231 
1232   memset(&cf2, 0, sizeof(cf2));
1233   cf2.cbSize = sizeof(cf2);
1234   cf2.dwMask = CFM_SPACING;
1235   cf2.sSpacing = 10;
1236   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1237   memset(&cf2, 0, sizeof(cf2));
1238   cf2.cbSize = sizeof(cf2);
1239   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1240   ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1241 
1242   /* show that wWeight is at the correct offset in CHARFORMAT2W */
1243   memset(&cfW, 0, sizeof(cfW));
1244   cfW.cbSize = sizeof(cfW);
1245   cfW.dwMask = CFM_WEIGHT;
1246   cfW.wWeight = 100;
1247   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1248   memset(&cfW, 0, sizeof(cfW));
1249   cfW.cbSize = sizeof(cfW);
1250   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1251   ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1252 
1253   memset(&cfW, 0, sizeof(cfW));
1254   cfW.cbSize = sizeof(cfW);
1255   cfW.dwMask = CFM_SPACING;
1256   cfW.sSpacing = 10;
1257   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1258   memset(&cfW, 0, sizeof(cfW));
1259   cfW.cbSize = sizeof(cfW);
1260   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1261   ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1262 
1263   /* test CFE_UNDERLINE and bUnderlineType interaction */
1264   /* clear bold, italic */
1265   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1266   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1267   cf2.cbSize = sizeof(CHARFORMAT2A);
1268   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1269   cf2.dwEffects = 0;
1270   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1271 
1272   /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1273   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1274   cf2.cbSize = sizeof(CHARFORMAT2A);
1275   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1276   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1277      "got %08x\n", cf2.dwMask);
1278   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1279   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1280 
1281   /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1282   cf2.dwMask = CFM_UNDERLINETYPE;
1283   cf2.bUnderlineType = CFU_UNDERLINE;
1284   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1285   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1286   cf2.cbSize = sizeof(CHARFORMAT2A);
1287   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1288   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1289      "got %08x\n", cf2.dwMask);
1290   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1291   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1292 
1293   /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1294   cf2.dwMask = CFM_UNDERLINETYPE;
1295   cf2.bUnderlineType = CFU_UNDERLINENONE;
1296   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1297   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1298   cf2.cbSize = sizeof(CHARFORMAT2A);
1299   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1300   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1301      "got %08x\n", cf2.dwMask);
1302   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1303   ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1304 
1305   /* another underline type also sets CFE_UNDERLINE */
1306   cf2.dwMask = CFM_UNDERLINETYPE;
1307   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1308   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1309   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1310   cf2.cbSize = sizeof(CHARFORMAT2A);
1311   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1312   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1313      "got %08x\n", cf2.dwMask);
1314   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1315   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1316 
1317   /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1318   cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1319   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1320   cf2.dwEffects = 0;
1321   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1322   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1323   cf2.cbSize = sizeof(CHARFORMAT2A);
1324   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1325   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1326      "got %08x\n", cf2.dwMask);
1327   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1328   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1329 
1330   /* And turing it back on again by just setting CFE_UNDERLINE */
1331   cf2.dwMask = CFM_UNDERLINE;
1332   cf2.dwEffects = CFE_UNDERLINE;
1333   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1334   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1335   cf2.cbSize = sizeof(CHARFORMAT2A);
1336   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1337   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1338      "got %08x\n", cf2.dwMask);
1339   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1340   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1341 
1342   DestroyWindow(hwndRichEdit);
1343 }
1344 
1345 static void test_EM_SETTEXTMODE(void)
1346 {
1347   HWND hwndRichEdit = new_richedit(NULL);
1348   CHARFORMAT2A cf2, cf2test;
1349   CHARRANGE cr;
1350   int rc = 0;
1351 
1352   /*Attempt to use mutually exclusive modes*/
1353   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1354   ok(rc == E_INVALIDARG,
1355      "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1356 
1357   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1358   /*Insert text into the control*/
1359 
1360   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1361 
1362   /*Attempt to change the control to plain text mode*/
1363   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1364   ok(rc == E_UNEXPECTED,
1365      "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1366 
1367   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1368   If rich text is pasted, it should have the same formatting as the rest
1369   of the text in the control*/
1370 
1371   /*Italicize the text
1372   *NOTE: If the default text was already italicized, the test will simply
1373   reverse; in other words, it will copy a regular "wine" into a plain
1374   text window that uses an italicized format*/
1375   cf2.cbSize = sizeof(CHARFORMAT2A);
1376   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1377 
1378   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1379   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1380 
1381   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1382   ok(rc == 0, "Text marked as modified, expected not modified!\n");
1383 
1384   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1385   however, SCF_ALL has been implemented*/
1386   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1387   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1388 
1389   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1390   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1391 
1392   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1393 
1394   /*Select the string "wine"*/
1395   cr.cpMin = 0;
1396   cr.cpMax = 4;
1397   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1398 
1399   /*Copy the italicized "wine" to the clipboard*/
1400   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1401 
1402   /*Reset the formatting to default*/
1403   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1404   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1405   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1406 
1407   /*Clear the text in the control*/
1408   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1409 
1410   /*Switch to Plain Text Mode*/
1411   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1412   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1413 
1414   /*Input "wine" again in normal format*/
1415   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1416 
1417   /*Paste the italicized "wine" into the control*/
1418   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1419 
1420   /*Select a character from the first "wine" string*/
1421   cr.cpMin = 2;
1422   cr.cpMax = 3;
1423   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1424 
1425   /*Retrieve its formatting*/
1426   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1427 
1428   /*Select a character from the second "wine" string*/
1429   cr.cpMin = 5;
1430   cr.cpMax = 6;
1431   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1432 
1433   /*Retrieve its formatting*/
1434   cf2test.cbSize = sizeof(CHARFORMAT2A);
1435   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1436 
1437   /*Compare the two formattings*/
1438     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1439       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1440        cf2.dwEffects, cf2test.dwEffects);
1441   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1442                          printing "wine" in the current format(normal)
1443                          pasting "wine" from the clipboard(italicized)
1444                          comparing the two formats(should differ)*/
1445 
1446   /*Attempt to switch with text in control*/
1447   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1448   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1449 
1450   /*Clear control*/
1451   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1452 
1453   /*Switch into Rich Text mode*/
1454   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1455   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1456 
1457   /*Print "wine" in normal formatting into the control*/
1458   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1459 
1460   /*Paste italicized "wine" into the control*/
1461   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1462 
1463   /*Select text from the first "wine" string*/
1464   cr.cpMin = 1;
1465   cr.cpMax = 3;
1466   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1467 
1468   /*Retrieve its formatting*/
1469   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1470 
1471   /*Select text from the second "wine" string*/
1472   cr.cpMin = 6;
1473   cr.cpMax = 7;
1474   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1475 
1476   /*Retrieve its formatting*/
1477   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1478 
1479   /*Test that the two formattings are not the same*/
1480   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1481       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1482       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1483 
1484   DestroyWindow(hwndRichEdit);
1485 }
1486 
1487 static void test_SETPARAFORMAT(void)
1488 {
1489   HWND hwndRichEdit = new_richedit(NULL);
1490   PARAFORMAT2 fmt;
1491   HRESULT ret;
1492   LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1493   fmt.cbSize = sizeof(PARAFORMAT2);
1494   fmt.dwMask = PFM_ALIGNMENT;
1495   fmt.wAlignment = PFA_LEFT;
1496 
1497   ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1498   ok(ret != 0, "expected non-zero got %d\n", ret);
1499 
1500   fmt.cbSize = sizeof(PARAFORMAT2);
1501   fmt.dwMask = -1;
1502   ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1503   /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1504    * between richedit different native builds of riched20.dll
1505    * used on different Windows versions. */
1506   ret &= ~PFM_TABLEROWDELIMITER;
1507   fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1508 
1509   ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1510   ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1511 
1512   /* Test some other paraformat field defaults */
1513   ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1514   ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1515   ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1516   ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1517 
1518   DestroyWindow(hwndRichEdit);
1519 }
1520 
1521 static void test_TM_PLAINTEXT(void)
1522 {
1523   /*Tests plain text properties*/
1524 
1525   HWND hwndRichEdit = new_richedit(NULL);
1526   CHARFORMAT2A cf2, cf2test;
1527   CHARRANGE cr;
1528   int rc = 0;
1529 
1530   /*Switch to plain text mode*/
1531 
1532   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1533   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1534 
1535   /*Fill control with text*/
1536 
1537   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1538 
1539   /*Select some text and bold it*/
1540 
1541   cr.cpMin = 10;
1542   cr.cpMax = 20;
1543   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1544   cf2.cbSize = sizeof(CHARFORMAT2A);
1545   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1546 
1547   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1548   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1549 
1550   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1551   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1552 
1553   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | 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_ALL, (LPARAM)&cf2);
1557   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1558 
1559   /*Get the formatting of those characters*/
1560 
1561   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1562 
1563   /*Get the formatting of some other characters*/
1564   cf2test.cbSize = sizeof(CHARFORMAT2A);
1565   cr.cpMin = 21;
1566   cr.cpMax = 30;
1567   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1568   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1569 
1570   /*Test that they are the same as plain text allows only one formatting*/
1571 
1572   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1573      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1574      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1575 
1576   /*Fill the control with a "wine" string, which when inserted will be bold*/
1577 
1578   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1579 
1580   /*Copy the bolded "wine" string*/
1581 
1582   cr.cpMin = 0;
1583   cr.cpMax = 4;
1584   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1585   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1586 
1587   /*Swap back to rich text*/
1588 
1589   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1590   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1591 
1592   /*Set the default formatting to bold italics*/
1593 
1594   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1595   cf2.dwMask |= CFM_ITALIC;
1596   cf2.dwEffects ^= CFE_ITALIC;
1597   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1598   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1599 
1600   /*Set the text in the control to "wine", which will be bold and italicized*/
1601 
1602   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1603 
1604   /*Paste the plain text "wine" string, which should take the insert
1605    formatting, which at the moment is bold italics*/
1606 
1607   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1608 
1609   /*Select the first "wine" string and retrieve its formatting*/
1610 
1611   cr.cpMin = 1;
1612   cr.cpMax = 3;
1613   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1614   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1615 
1616   /*Select the second "wine" string and retrieve its formatting*/
1617 
1618   cr.cpMin = 5;
1619   cr.cpMax = 7;
1620   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1621   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1622 
1623   /*Compare the two formattings. They should be the same.*/
1624 
1625   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1626      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1627      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1628   DestroyWindow(hwndRichEdit);
1629 }
1630 
1631 static void test_WM_GETTEXT(void)
1632 {
1633     HWND hwndRichEdit = new_richedit(NULL);
1634     static const char text[] = "Hello. My name is RichEdit!";
1635     static const char text2[] = "Hello. My name is RichEdit!\r";
1636     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1637     char buffer[1024] = {0};
1638     int result;
1639 
1640     /* Baseline test with normal-sized buffer */
1641     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1642     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1643     ok(result == lstrlenA(buffer),
1644         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1645     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1646     result = strcmp(buffer,text);
1647     ok(result == 0,
1648         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1649 
1650     /* Test for returned value of WM_GETTEXTLENGTH */
1651     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1652     ok(result == lstrlenA(text),
1653         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1654         result, lstrlenA(text));
1655 
1656     /* Test for behavior in overflow case */
1657     memset(buffer, 0, 1024);
1658     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1659     ok(result == 0 ||
1660        result == lstrlenA(text) - 1, /* XP, win2k3 */
1661         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1662     result = strcmp(buffer,text);
1663     if (result)
1664         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1665     ok(result == 0,
1666         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1667 
1668     /* Baseline test with normal-sized buffer and carriage return */
1669     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1670     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1671     ok(result == lstrlenA(buffer),
1672         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1673     result = strcmp(buffer,text2_after);
1674     ok(result == 0,
1675         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1676 
1677     /* Test for returned value of WM_GETTEXTLENGTH */
1678     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1679     ok(result == lstrlenA(text2_after),
1680         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1681         result, lstrlenA(text2_after));
1682 
1683     /* Test for behavior of CRLF conversion in case of overflow */
1684     memset(buffer, 0, 1024);
1685     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1686     ok(result == 0 ||
1687        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1688         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1689     result = strcmp(buffer,text2);
1690     if (result)
1691         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1692     ok(result == 0,
1693         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1694 
1695     DestroyWindow(hwndRichEdit);
1696 }
1697 
1698 static void test_EM_GETTEXTRANGE(void)
1699 {
1700     HWND hwndRichEdit = new_richedit(NULL);
1701     const char * text1 = "foo bar\r\nfoo bar";
1702     const char * text2 = "foo bar\rfoo bar";
1703     const char * expect = "bar\rfoo";
1704     char buffer[1024] = {0};
1705     LRESULT result;
1706     TEXTRANGEA textRange;
1707 
1708     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1709 
1710     textRange.lpstrText = buffer;
1711     textRange.chrg.cpMin = 4;
1712     textRange.chrg.cpMax = 11;
1713     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1714     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1715     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1716 
1717     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1718 
1719     textRange.lpstrText = buffer;
1720     textRange.chrg.cpMin = 4;
1721     textRange.chrg.cpMax = 11;
1722     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1723     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1724     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1725 
1726     /* cpMax of text length is used instead of -1 in this case */
1727     textRange.lpstrText = buffer;
1728     textRange.chrg.cpMin = 0;
1729     textRange.chrg.cpMax = -1;
1730     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1731     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1732     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1733 
1734     /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1735     textRange.lpstrText = buffer;
1736     textRange.chrg.cpMin = -1;
1737     textRange.chrg.cpMax = 1;
1738     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1739     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1740     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1741 
1742     /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1743     textRange.lpstrText = buffer;
1744     textRange.chrg.cpMin = 1;
1745     textRange.chrg.cpMax = -1;
1746     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1747     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1748     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1749 
1750     /* no end character is copied if cpMax - cpMin < 0 */
1751     textRange.lpstrText = buffer;
1752     textRange.chrg.cpMin = 5;
1753     textRange.chrg.cpMax = 5;
1754     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1755     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1756     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1757 
1758     /* cpMax of text length is used if cpMax > text length*/
1759     textRange.lpstrText = buffer;
1760     textRange.chrg.cpMin = 0;
1761     textRange.chrg.cpMax = 1000;
1762     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1763     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1764     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1765 
1766     /* Test with multibyte character */
1767     if (!is_lang_japanese)
1768         skip("Skip multibyte character tests on non-Japanese platform\n");
1769     else
1770     {
1771         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1772         textRange.chrg.cpMin = 4;
1773         textRange.chrg.cpMax = 8;
1774         result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1775         todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1776         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1777     }
1778 
1779     DestroyWindow(hwndRichEdit);
1780 }
1781 
1782 static void test_EM_GETSELTEXT(void)
1783 {
1784     HWND hwndRichEdit = new_richedit(NULL);
1785     const char * text1 = "foo bar\r\nfoo bar";
1786     const char * text2 = "foo bar\rfoo bar";
1787     const char * expect = "bar\rfoo";
1788     char buffer[1024] = {0};
1789     LRESULT result;
1790 
1791     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1792 
1793     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1794     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1795     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1796     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1797 
1798     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1799 
1800     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1801     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1802     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1803     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1804 
1805     /* Test with multibyte character */
1806     if (!is_lang_japanese)
1807         skip("Skip multibyte character tests on non-Japanese platform\n");
1808     else
1809     {
1810         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1811         SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1812         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1813         todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1814         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1815     }
1816 
1817     DestroyWindow(hwndRichEdit);
1818 }
1819 
1820 /* FIXME: need to test unimplemented options and robustly test wparam */
1821 static void test_EM_SETOPTIONS(void)
1822 {
1823     HWND hwndRichEdit;
1824     static const char text[] = "Hello. My name is RichEdit!";
1825     char buffer[1024] = {0};
1826     DWORD dwStyle, options, oldOptions;
1827     DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1828                          ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1829                          ES_SELECTIONBAR|ES_VERTICAL;
1830 
1831     /* Test initial options. */
1832     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1833                                 0, 0, 200, 60, NULL, NULL,
1834                                 hmoduleRichEdit, NULL);
1835     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1836        RICHEDIT_CLASS20A, (int) GetLastError());
1837     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1838     ok(options == 0, "Incorrect initial options %x\n", options);
1839     DestroyWindow(hwndRichEdit);
1840 
1841     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1842                                 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1843                                 0, 0, 200, 60, NULL, NULL,
1844                                 hmoduleRichEdit, NULL);
1845     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1846        RICHEDIT_CLASS20A, (int) GetLastError());
1847     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1848     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1849     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1850        "Incorrect initial options %x\n", options);
1851 
1852     /* NEGATIVE TESTING - NO OPTIONS SET */
1853     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1854     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1855 
1856     /* testing no readonly by sending 'a' to the control*/
1857     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1858     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1859     ok(buffer[0]=='a',
1860        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1861     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1862 
1863     /* READONLY - sending 'a' to the control */
1864     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1865     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1866     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1867     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1868     ok(buffer[0]==text[0],
1869        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1870 
1871     /* EM_SETOPTIONS changes the window style, but changing the
1872      * window style does not change the options. */
1873     dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1874     ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1875     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1876     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1877     ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1878     /* Confirm that the text is still read only. */
1879     SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1880     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1881     ok(buffer[0]==text[0],
1882        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1883 
1884     oldOptions = options;
1885     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1886     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1887     ok(options == oldOptions,
1888        "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1889 
1890     DestroyWindow(hwndRichEdit);
1891 }
1892 
1893 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1894 {
1895   CHARFORMAT2A text_format;
1896   text_format.cbSize = sizeof(text_format);
1897   SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1898   SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1899   return (text_format.dwEffects & CFE_LINK) != 0;
1900 }
1901 
1902 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1903 {
1904   BOOL link_present = FALSE;
1905 
1906   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1907   if (is_url)
1908   { /* control text is url; should get CFE_LINK */
1909     ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1910   }
1911   else
1912   {
1913     ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1914   }
1915 }
1916 
1917 static HWND new_static_wnd(HWND parent) {
1918   return new_window("Static", 0, parent);
1919 }
1920 
1921 static void test_EM_AUTOURLDETECT(void)
1922 {
1923   /* DO NOT change the properties of the first two elements. To shorten the
1924      tests, all tests after WM_SETTEXT test just the first two elements -
1925      one non-URL and one URL */
1926   struct urls_s {
1927     const char *text;
1928     BOOL is_url;
1929   } urls[12] = {
1930     {"winehq.org", FALSE},
1931     {"http://www.winehq.org", TRUE},
1932     {"http//winehq.org", FALSE},
1933     {"ww.winehq.org", FALSE},
1934     {"www.winehq.org", TRUE},
1935     {"ftp://192.168.1.1", TRUE},
1936     {"ftp//192.168.1.1", FALSE},
1937     {"mailto:your@email.com", TRUE},
1938     {"prospero:prosperoserver", TRUE},
1939     {"telnet:test", TRUE},
1940     {"news:newserver", TRUE},
1941     {"wais:waisserver", TRUE}
1942   };
1943 
1944   int i, j;
1945   int urlRet=-1;
1946   HWND hwndRichEdit, parent;
1947 
1948   /* All of the following should cause the URL to be detected  */
1949   const char * templates_delim[] = {
1950     "This is some text with X on it",
1951     "This is some text with (X) on it",
1952     "This is some text with X\r on it",
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' on it",
1956     "This is some text with 'X' on it",
1957     "This is some text with :X: on it",
1958 
1959     "This text ends with X",
1960 
1961     "This is some text with X) on it",
1962     "This is some text with X--- on it",
1963     "This is some text with X\" on it",
1964     "This is some text with X' on it",
1965     "This is some text with X: on it",
1966 
1967     "This is some text with (X on it",
1968     "This is some text with \rX on it",
1969     "This is some text with ---X on it",
1970     "This is some text with \"X on it",
1971     "This is some text with 'X on it",
1972     "This is some text with :X on it",
1973   };
1974   /* None of these should cause the URL to be detected */
1975   const char * templates_non_delim[] = {
1976     "This is some text with |X| on it",
1977     "This is some text with *X* on it",
1978     "This is some text with /X/ on it",
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   };
1994   /* All of these cause the URL detection to be extended by one more byte,
1995      thus demonstrating that the tested character is considered as part
1996      of the URL. */
1997   const char * templates_xten_delim[] = {
1998     "This is some text with X| on it",
1999     "This is some text with X* on it",
2000     "This is some text with X/ on it",
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   };
2008   /* These delims act as neutral breaks.  Whether the url is ended
2009      or not depends on the next non-neutral character.  We'll test
2010      with Y unchanged, in which case the url should include the
2011      deliminator and the Y.  We'll also test with the Y changed
2012      to a space, in which case the url stops before the
2013      deliminator. */
2014   const char * templates_neutral_delim[] = {
2015     "This is some text with X-Y on it",
2016     "This is some text with X--Y on it",
2017     "This is some text with X!Y on it",
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   };
2034   char buffer[1024];
2035 
2036   parent = new_static_wnd(NULL);
2037   hwndRichEdit = new_richedit(parent);
2038   /* Try and pass EM_AUTOURLDETECT some test wParam values */
2039   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2040   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2041   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2042   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2043   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2044   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2045   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2046   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2047   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2048   /* for each url, check the text to see if CFE_LINK effect is present */
2049   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2050 
2051     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2052     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2053     check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2054 
2055     /* Link detection should happen immediately upon WM_SETTEXT */
2056     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2057     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2058     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2059   }
2060   DestroyWindow(hwndRichEdit);
2061 
2062   /* Test detection of URLs within normal text - WM_SETTEXT case. */
2063   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
2064     hwndRichEdit = new_richedit(parent);
2065 
2066     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2067       char * at_pos;
2068       int at_offset;
2069       int end_offset;
2070 
2071       at_pos = strchr(templates_delim[j], 'X');
2072       at_offset = at_pos - templates_delim[j];
2073       memcpy(buffer, templates_delim[j], at_offset);
2074       buffer[at_offset] = '\0';
2075       strcat(buffer, urls[i].text);
2076       strcat(buffer, templates_delim[j] + at_offset + 1);
2077       end_offset = at_offset + strlen(urls[i].text);
2078 
2079       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2080       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2081 
2082       /* This assumes no templates start with the URL itself, and that they
2083          have at least two characters before the URL text */
2084       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2085         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2086       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2087         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2088       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2089         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2090 
2091       if (urls[i].is_url)
2092       {
2093         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2094           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2095         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2096           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2097       }
2098       else
2099       {
2100         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2101           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2102         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2103           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2104       }
2105       if (buffer[end_offset] != '\0')
2106       {
2107         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2108           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2109         if (buffer[end_offset +1] != '\0')
2110         {
2111           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2112             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2113         }
2114       }
2115     }
2116 
2117     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
2118       char * at_pos;
2119       int at_offset;
2120       int end_offset;
2121 
2122       at_pos = strchr(templates_non_delim[j], 'X');
2123       at_offset = at_pos - templates_non_delim[j];
2124       memcpy(buffer, templates_non_delim[j], at_offset);
2125       buffer[at_offset] = '\0';
2126       strcat(buffer, urls[i].text);
2127       strcat(buffer, templates_non_delim[j] + at_offset + 1);
2128       end_offset = at_offset + strlen(urls[i].text);
2129 
2130       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2131       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2132 
2133       /* This assumes no templates start with the URL itself, and that they
2134          have at least two characters before the URL text */
2135       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2136         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2137       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2138         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2139       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2140         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2141 
2142       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2143         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2144       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2145         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2146       if (buffer[end_offset] != '\0')
2147       {
2148         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2149           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2150         if (buffer[end_offset +1] != '\0')
2151         {
2152           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2153             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2154         }
2155       }
2156     }
2157 
2158     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
2159       char * at_pos;
2160       int at_offset;
2161       int end_offset;
2162 
2163       at_pos = strchr(templates_xten_delim[j], 'X');
2164       at_offset = at_pos - templates_xten_delim[j];
2165       memcpy(buffer, templates_xten_delim[j], at_offset);
2166       buffer[at_offset] = '\0';
2167       strcat(buffer, urls[i].text);
2168       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2169       end_offset = at_offset + strlen(urls[i].text);
2170 
2171       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2172       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2173 
2174       /* This assumes no templates start with the URL itself, and that they
2175          have at least two characters before the URL text */
2176       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2177         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2178       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2179         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2180       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2181         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2182 
2183       if (urls[i].is_url)
2184       {
2185         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2186           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2187         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2188           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2189         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2190           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2191       }
2192       else
2193       {
2194         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2195           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2196         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2197           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2198         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2199           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2200       }
2201       if (buffer[end_offset +1] != '\0')
2202       {
2203         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2204           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2205         if (buffer[end_offset +2] != '\0')
2206         {
2207           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2208             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2209         }
2210       }
2211     }
2212 
2213     for (j = 0; j < sizeof(templates_neutral_delim) / sizeof(const char *); j++) {
2214       char * at_pos, * end_pos;
2215       int at_offset;
2216       int end_offset;
2217 
2218       if (!urls[i].is_url) continue;
2219 
2220       at_pos = strchr(templates_neutral_delim[j], 'X');
2221       at_offset = at_pos - templates_neutral_delim[j];
2222       memcpy(buffer, templates_neutral_delim[j], at_offset);
2223       buffer[at_offset] = '\0';
2224       strcat(buffer, urls[i].text);
2225       strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2226 
2227       end_pos = strchr(buffer, 'Y');
2228       end_offset = end_pos - buffer;
2229 
2230       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2231       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2232 
2233       /* This assumes no templates start with the URL itself, and that they
2234          have at least two characters before the URL text */
2235       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2236          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2237       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2238          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2239       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2240          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2241 
2242       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2243          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2244       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2245          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2246       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2247          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2248 
2249       *end_pos = ' ';
2250 
2251       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2252       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2253 
2254       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2255          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2256       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2257          "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2258       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2259          "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2260     }
2261 
2262     DestroyWindow(hwndRichEdit);
2263     hwndRichEdit = NULL;
2264   }
2265 
2266   /* Test detection of URLs within normal text - WM_CHAR case. */
2267   /* Test only the first two URL examples for brevity */
2268   for (i = 0; i < 2; i++) {
2269     hwndRichEdit = new_richedit(parent);
2270 
2271     /* Also for brevity, test only the first three delimiters */
2272     for (j = 0; j < 3; j++) {
2273       char * at_pos;
2274       int at_offset;
2275       int end_offset;
2276       int u, v;
2277 
2278       at_pos = strchr(templates_delim[j], 'X');
2279       at_offset = at_pos - templates_delim[j];
2280       end_offset = at_offset + strlen(urls[i].text);
2281 
2282       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2283       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2284       for (u = 0; templates_delim[j][u]; u++) {
2285         if (templates_delim[j][u] == '\r') {
2286           simulate_typing_characters(hwndRichEdit, "\r");
2287         } else if (templates_delim[j][u] != 'X') {
2288           SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2289         } else {
2290           for (v = 0; urls[i].text[v]; v++) {
2291             SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2292           }
2293         }
2294       }
2295       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2296 
2297       /* This assumes no templates start with the URL itself, and that they
2298          have at least two characters before the URL text */
2299       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2300         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2301       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2302         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2303       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2304         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2305 
2306       if (urls[i].is_url)
2307       {
2308         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2309           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2310         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2311           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2312       }
2313       else
2314       {
2315         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2316           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2317         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2318           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2319       }
2320       if (buffer[end_offset] != '\0')
2321       {
2322         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2323           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2324         if (buffer[end_offset +1] != '\0')
2325         {
2326           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2327             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2328         }
2329       }
2330 
2331       /* The following will insert a paragraph break after the first character
2332          of the URL candidate, thus breaking the URL. It is expected that the
2333          CFE_LINK attribute should break across both pieces of the URL */
2334       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2335       simulate_typing_characters(hwndRichEdit, "\r");
2336       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2337 
2338       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2339         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2340       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2341         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2342       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2343         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2344 
2345       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2346         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2347       /* end_offset moved because of paragraph break */
2348       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2349         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2350       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2351       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
2352       {
2353         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2354           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2355         if (buffer[end_offset +2] != '\0')
2356         {
2357           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2358             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2359         }
2360       }
2361 
2362       /* The following will remove the just-inserted paragraph break, thus
2363          restoring the URL */
2364       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2365       simulate_typing_characters(hwndRichEdit, "\b");
2366       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2367 
2368       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2369         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2370       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2371         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2372       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2373         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2374 
2375       if (urls[i].is_url)
2376       {
2377         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2378           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2379         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2380           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2381       }
2382       else
2383       {
2384         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2385           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2386         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2387           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2388       }
2389       if (buffer[end_offset] != '\0')
2390       {
2391         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2392           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2393         if (buffer[end_offset +1] != '\0')
2394         {
2395           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2396             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2397         }
2398       }
2399     }
2400     DestroyWindow(hwndRichEdit);
2401     hwndRichEdit = NULL;
2402   }
2403 
2404   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2405   /* Test just the first two URL examples for brevity */
2406   for (i = 0; i < 2; i++) {
2407     SETTEXTEX st;
2408 
2409     hwndRichEdit = new_richedit(parent);
2410 
2411     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2412        be detected:
2413        1) Set entire text, a la WM_SETTEXT
2414        2) Set a selection of the text to the URL
2415        3) Set a portion of the text at a time, which eventually results in
2416           an URL
2417        All of them should give equivalent results
2418      */
2419 
2420     /* Set entire text in one go, like WM_SETTEXT */
2421     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2422       char * at_pos;
2423       int at_offset;
2424       int end_offset;
2425 
2426       st.codepage = CP_ACP;
2427       st.flags = ST_DEFAULT;
2428 
2429       at_pos = strchr(templates_delim[j], 'X');
2430       at_offset = at_pos - templates_delim[j];
2431       memcpy(buffer, templates_delim[j], at_offset);
2432       buffer[at_offset] = '\0';
2433       strcat(buffer, urls[i].text);
2434       strcat(buffer, templates_delim[j] + at_offset + 1);
2435       end_offset = at_offset + strlen(urls[i].text);
2436 
2437       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2438       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2439 
2440       /* This assumes no templates start with the URL itself, and that they
2441          have at least two characters before the URL text */
2442       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2443         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2444       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2445         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2446       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2447         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2448 
2449       if (urls[i].is_url)
2450       {
2451         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2452           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2453         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2454           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2455       }
2456       else
2457       {
2458         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2459           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2460         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2461           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2462       }
2463       if (buffer[end_offset] != '\0')
2464       {
2465         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2466           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2467         if (buffer[end_offset +1] != '\0')
2468         {
2469           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2470             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2471         }
2472       }
2473     }
2474 
2475     /* Set selection with X to the URL */
2476     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2477       char * at_pos;
2478       int at_offset;
2479       int end_offset;
2480 
2481       at_pos = strchr(templates_delim[j], 'X');
2482       at_offset = at_pos - templates_delim[j];
2483       end_offset = at_offset + strlen(urls[i].text);
2484 
2485       st.codepage = CP_ACP;
2486       st.flags = ST_DEFAULT;
2487       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2488       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2489       st.flags = ST_SELECTION;
2490       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2491       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2492       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2493 
2494       /* This assumes no templates start with the URL itself, and that they
2495          have at least two characters before the URL text */
2496       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2497         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2498       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2499         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2500       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2501         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2502 
2503       if (urls[i].is_url)
2504       {
2505         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2506           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2507         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2508           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2509       }
2510       else
2511       {
2512         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2513           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2514         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2515           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2516       }
2517       if (buffer[end_offset] != '\0')
2518       {
2519         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2520           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2521         if (buffer[end_offset +1] != '\0')
2522         {
2523           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2524             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2525         }
2526       }
2527     }
2528 
2529     /* Set selection with X to the first character of the URL, then the rest */
2530     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2531       char * at_pos;
2532       int at_offset;
2533       int end_offset;
2534 
2535       at_pos = strchr(templates_delim[j], 'X');
2536       at_offset = at_pos - templates_delim[j];
2537       end_offset = at_offset + strlen(urls[i].text);
2538 
2539       strcpy(buffer, "YY");
2540       buffer[0] = urls[i].text[0];
2541 
2542       st.codepage = CP_ACP;
2543       st.flags = ST_DEFAULT;
2544       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2545       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2546       st.flags = ST_SELECTION;
2547       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2548       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2549       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2550       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2551       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2552 
2553       /* This assumes no templates start with the URL itself, and that they
2554          have at least two characters before the URL text */
2555       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2556         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2557       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2558         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2559       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2560         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2561 
2562       if (urls[i].is_url)
2563       {
2564         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2565           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2566         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2567           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2568       }
2569       else
2570       {
2571         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2572           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2573         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2574           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2575       }
2576       if (buffer[end_offset] != '\0')
2577       {
2578         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2579           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2580         if (buffer[end_offset +1] != '\0')
2581         {
2582           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2583             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2584         }
2585       }
2586     }
2587 
2588     DestroyWindow(hwndRichEdit);
2589     hwndRichEdit = NULL;
2590   }
2591 
2592   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2593   /* Test just the first two URL examples for brevity */
2594   for (i = 0; i < 2; i++) {
2595     hwndRichEdit = new_richedit(parent);
2596 
2597     /* Set selection with X to the URL */
2598     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2599       char * at_pos;
2600       int at_offset;
2601       int end_offset;
2602 
2603       at_pos = strchr(templates_delim[j], 'X');
2604       at_offset = at_pos - templates_delim[j];
2605       end_offset = at_offset + strlen(urls[i].text);
2606 
2607       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2608       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2609       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2610       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2611       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2612 
2613       /* This assumes no templates start with the URL itself, and that they
2614          have at least two characters before the URL text */
2615       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2616         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2617       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2618         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2619       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2620         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2621 
2622       if (urls[i].is_url)
2623       {
2624         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2625           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2626         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2627           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2628       }
2629       else
2630       {
2631         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2632           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2633         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2634           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2635       }
2636       if (buffer[end_offset] != '\0')
2637       {
2638         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2639           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2640         if (buffer[end_offset +1] != '\0')
2641         {
2642           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2643             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2644         }
2645       }
2646     }
2647 
2648     /* Set selection with X to the first character of the URL, then the rest */
2649     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2650       char * at_pos;
2651       int at_offset;
2652       int end_offset;
2653 
2654       at_pos = strchr(templates_delim[j], 'X');
2655       at_offset = at_pos - templates_delim[j];
2656       end_offset = at_offset + strlen(urls[i].text);
2657 
2658       strcpy(buffer, "YY");
2659       buffer[0] = urls[i].text[0];
2660 
2661       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2662       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2663       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2664       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2665       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2666       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2667       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2668 
2669       /* This assumes no templates start with the URL itself, and that they
2670          have at least two characters before the URL text */
2671       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2672         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2673       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2674         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2675       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2676         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2677 
2678       if (urls[i].is_url)
2679       {
2680         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2681           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2682         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2683           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2684       }
2685       else
2686       {
2687         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2688           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2689         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2690           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2691       }
2692       if (buffer[end_offset] != '\0')
2693       {
2694         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2695           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2696         if (buffer[end_offset +1] != '\0')
2697         {
2698           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2699             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2700         }
2701       }
2702     }
2703 
2704     DestroyWindow(hwndRichEdit);
2705     hwndRichEdit = NULL;
2706   }
2707 
2708   DestroyWindow(parent);
2709 }
2710 
2711 static void test_EM_SCROLL(void)
2712 {
2713   int i, j;
2714   int r; /* return value */
2715   int expr; /* expected return value */
2716   HWND hwndRichEdit = new_richedit(NULL);
2717   int y_before, y_after; /* units of lines of text */
2718 
2719   /* test a richedit box containing a single line of text */
2720   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2721   expr = 0x00010000;
2722   for (i = 0; i < 4; i++) {
2723     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2724 
2725     r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2726     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2727     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2728        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2729     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2730        "(i == %d)\n", y_after, i);
2731   }
2732 
2733   /*
2734    * test a richedit box that will scroll. There are two general
2735    * cases: the case without any long lines and the case with a long
2736    * line.
2737    */
2738   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2739     if (i == 0)
2740       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2741     else
2742       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2743                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2744                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2745                   "LONG LINE \nb\nc\nd\ne");
2746     for (j = 0; j < 12; j++) /* reset scroll position to top */
2747       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2748 
2749     /* get first visible line */
2750     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2751     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2752 
2753     /* get new current first visible line */
2754     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2755 
2756     ok(((r & 0xffffff00) == 0x00010000) &&
2757        ((r & 0x000000ff) != 0x00000000),
2758        "EM_SCROLL page down didn't scroll by a small positive number of "
2759        "lines (r == 0x%08x)\n", r);
2760     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2761        "(line %d scrolled to line %d\n", y_before, y_after);
2762 
2763     y_before = y_after;
2764 
2765     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2766     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2767     ok(((r & 0xffffff00) == 0x0001ff00),
2768        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2769        "(r == 0x%08x)\n", r);
2770     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2771        "%d scrolled to line %d\n", y_before, y_after);
2772 
2773     y_before = y_after;
2774 
2775     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2776 
2777     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2778 
2779     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2780        "(r == 0x%08x)\n", r);
2781     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2782        "1 line (%d scrolled to %d)\n", y_before, y_after);
2783 
2784     y_before = y_after;
2785 
2786     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2787 
2788     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2789 
2790     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2791        "(r == 0x%08x)\n", r);
2792     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2793        "line (%d scrolled to %d)\n", y_before, y_after);
2794 
2795     y_before = y_after;
2796 
2797     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2798                     SB_LINEUP, 0); /* lineup beyond top */
2799 
2800     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2801 
2802     ok(r == 0x00010000,
2803        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2804     ok(y_before == y_after,
2805        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2806 
2807     y_before = y_after;
2808 
2809     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2810                     SB_PAGEUP, 0);/*page up beyond top */
2811 
2812     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2813 
2814     ok(r == 0x00010000,
2815        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2816     ok(y_before == y_after,
2817        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2818 
2819     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2820       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2821     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2822     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2823                     SB_PAGEDOWN, 0); /* page down beyond bot */
2824     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2825 
2826     ok(r == 0x00010000,
2827        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2828     ok(y_before == y_after,
2829        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2830        y_before, y_after);
2831 
2832     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2833     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2834     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2835 
2836     ok(r == 0x00010000,
2837        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2838     ok(y_before == y_after,
2839        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2840        y_before, y_after);
2841   }
2842   DestroyWindow(hwndRichEdit);
2843 }
2844 
2845 static unsigned int recursionLevel = 0;
2846 static unsigned int WM_SIZE_recursionLevel = 0;
2847 static BOOL bailedOutOfRecursion = FALSE;
2848 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2849 
2850 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2851 {
2852     LRESULT r;
2853 
2854     if (bailedOutOfRecursion) return 0;
2855     if (recursionLevel >= 32) {
2856         bailedOutOfRecursion = TRUE;
2857         return 0;
2858     }
2859 
2860     recursionLevel++;
2861     switch (message) {
2862     case WM_SIZE:
2863         WM_SIZE_recursionLevel++;
2864         r = richeditProc(hwnd, message, wParam, lParam);
2865         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2866         ShowScrollBar(hwnd, SB_VERT, TRUE);
2867         WM_SIZE_recursionLevel--;
2868         break;
2869     default:
2870         r = richeditProc(hwnd, message, wParam, lParam);
2871         break;
2872     }
2873     recursionLevel--;
2874     return r;
2875 }
2876 
2877 static void test_scrollbar_visibility(void)
2878 {
2879   HWND hwndRichEdit;
2880   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2881   SCROLLINFO si;
2882   WNDCLASSA cls;
2883   BOOL r;
2884 
2885   /* These tests show that richedit should temporarily refrain from automatically
2886      hiding or showing its scrollbars (vertical at least) when an explicit request
2887      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2888      Some applications depend on forced showing (when otherwise richedit would
2889      hide the vertical scrollbar) and are thrown on an endless recursive loop
2890      if richedit auto-hides the scrollbar again. Apparently they never heard of
2891      the ES_DISABLENOSCROLL style... */
2892 
2893   hwndRichEdit = new_richedit(NULL);
2894 
2895   /* Test default scrollbar visibility behavior */
2896   memset(&si, 0, sizeof(si));
2897   si.cbSize = sizeof(si);
2898   si.fMask = SIF_PAGE | SIF_RANGE;
2899   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2901     "Vertical scrollbar is visible, should be invisible.\n");
2902   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2903         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2904         si.nPage, si.nMin, si.nMax);
2905 
2906   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2907   memset(&si, 0, sizeof(si));
2908   si.cbSize = sizeof(si);
2909   si.fMask = SIF_PAGE | SIF_RANGE;
2910   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2911   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2912     "Vertical scrollbar is visible, should be invisible.\n");
2913   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2914         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2915         si.nPage, si.nMin, si.nMax);
2916 
2917   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2918   memset(&si, 0, sizeof(si));
2919   si.cbSize = sizeof(si);
2920   si.fMask = SIF_PAGE | SIF_RANGE;
2921   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2922   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2923     "Vertical scrollbar is invisible, should be visible.\n");
2924   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2925         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2926         si.nPage, si.nMin, si.nMax);
2927 
2928   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2929      even though it hides the scrollbar */
2930   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2931   memset(&si, 0, sizeof(si));
2932   si.cbSize = sizeof(si);
2933   si.fMask = SIF_PAGE | SIF_RANGE;
2934   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2936     "Vertical scrollbar is visible, should be invisible.\n");
2937   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2938         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2939         si.nPage, si.nMin, si.nMax);
2940 
2941   /* Setting non-scrolling text again does *not* reset scrollbar range */
2942   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2943   memset(&si, 0, sizeof(si));
2944   si.cbSize = sizeof(si);
2945   si.fMask = SIF_PAGE | SIF_RANGE;
2946   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2948     "Vertical scrollbar is visible, should be invisible.\n");
2949   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2950         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2951         si.nPage, si.nMin, si.nMax);
2952 
2953   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2954   memset(&si, 0, sizeof(si));
2955   si.cbSize = sizeof(si);
2956   si.fMask = SIF_PAGE | SIF_RANGE;
2957   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2958   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2959     "Vertical scrollbar is visible, should be invisible.\n");
2960   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2961         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2962         si.nPage, si.nMin, si.nMax);
2963 
2964   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2965   memset(&si, 0, sizeof(si));
2966   si.cbSize = sizeof(si);
2967   si.fMask = SIF_PAGE | SIF_RANGE;
2968   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2969   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2970     "Vertical scrollbar is visible, should be invisible.\n");
2971   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2972         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2973         si.nPage, si.nMin, si.nMax);
2974 
2975   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2976   memset(&si, 0, sizeof(si));
2977   si.cbSize = sizeof(si);
2978   si.fMask = SIF_PAGE | SIF_RANGE;
2979   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2981     "Vertical scrollbar is visible, should be invisible.\n");
2982   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2983         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2984         si.nPage, si.nMin, si.nMax);
2985 
2986   DestroyWindow(hwndRichEdit);
2987 
2988   /* Test again, with ES_DISABLENOSCROLL style */
2989   hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2990 
2991   /* Test default scrollbar visibility behavior */
2992   memset(&si, 0, sizeof(si));
2993   si.cbSize = sizeof(si);
2994   si.fMask = SIF_PAGE | SIF_RANGE;
2995   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2996   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2997     "Vertical scrollbar is invisible, should be visible.\n");
2998   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2999         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3000         si.nPage, si.nMin, si.nMax);
3001 
3002   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3003   memset(&si, 0, sizeof(si));
3004   si.cbSize = sizeof(si);
3005   si.fMask = SIF_PAGE | SIF_RANGE;
3006   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008     "Vertical scrollbar is invisible, should be visible.\n");
3009   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3010         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3011         si.nPage, si.nMin, si.nMax);
3012 
3013   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3014   memset(&si, 0, sizeof(si));
3015   si.cbSize = sizeof(si);
3016   si.fMask = SIF_PAGE | SIF_RANGE;
3017   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3018   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3019     "Vertical scrollbar is invisible, should be visible.\n");
3020   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3021         "reported page/range is %d (%d..%d)\n",
3022         si.nPage, si.nMin, si.nMax);
3023 
3024   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3025   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3026   memset(&si, 0, sizeof(si));
3027   si.cbSize = sizeof(si);
3028   si.fMask = SIF_PAGE | SIF_RANGE;
3029   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3030   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3031     "Vertical scrollbar is invisible, should be visible.\n");
3032   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3033         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3034         si.nPage, si.nMin, si.nMax);
3035 
3036   /* Setting non-scrolling text again does *not* reset scrollbar range */
3037   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3038   memset(&si, 0, sizeof(si));
3039   si.cbSize = sizeof(si);
3040   si.fMask = SIF_PAGE | SIF_RANGE;
3041   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3042   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3043     "Vertical scrollbar is invisible, should be visible.\n");
3044   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3045         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3046         si.nPage, si.nMin, si.nMax);
3047 
3048   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3049   memset(&si, 0, sizeof(si));
3050   si.cbSize = sizeof(si);
3051   si.fMask = SIF_PAGE | SIF_RANGE;
3052   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3053   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3054     "Vertical scrollbar is invisible, should be visible.\n");
3055   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3056         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3057         si.nPage, si.nMin, si.nMax);
3058 
3059   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3060   memset(&si, 0, sizeof(si));
3061   si.cbSize = sizeof(si);
3062   si.fMask = SIF_PAGE | SIF_RANGE;
3063   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3064   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3065     "Vertical scrollbar is invisible, should be visible.\n");
3066   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3067         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3068         si.nPage, si.nMin, si.nMax);
3069 
3070   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3071   memset(&si, 0, sizeof(si));
3072   si.cbSize = sizeof(si);
3073   si.fMask = SIF_PAGE | SIF_RANGE;
3074   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3075   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3076     "Vertical scrollbar is invisible, should be visible.\n");
3077   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3078         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3079         si.nPage, si.nMin, si.nMax);
3080 
3081   DestroyWindow(hwndRichEdit);
3082 
3083   /* Test behavior with explicit visibility request, using ShowScrollBar() */
3084   hwndRichEdit = new_richedit(NULL);
3085 
3086   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3087   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3088   memset(&si, 0, sizeof(si));
3089   si.cbSize = sizeof(si);
3090   si.fMask = SIF_PAGE | SIF_RANGE;
3091   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3092   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3093     "Vertical scrollbar is invisible, should be visible.\n");
3094   todo_wine {
3095   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3096         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3097         si.nPage, si.nMin, si.nMax);
3098   }
3099 
3100   /* Ditto, see above */
3101   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3102   memset(&si, 0, sizeof(si));
3103   si.cbSize = sizeof(si);
3104   si.fMask = SIF_PAGE | SIF_RANGE;
3105   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3106   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3107     "Vertical scrollbar is invisible, should be visible.\n");
3108   todo_wine {
3109   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3110         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3111         si.nPage, si.nMin, si.nMax);
3112   }
3113 
3114   /* Ditto, see above */
3115   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3116   memset(&si, 0, sizeof(si));
3117   si.cbSize = sizeof(si);
3118   si.fMask = SIF_PAGE | SIF_RANGE;
3119   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3120   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3121     "Vertical scrollbar is invisible, should be visible.\n");
3122   todo_wine {
3123   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3124         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3125         si.nPage, si.nMin, si.nMax);
3126   }
3127 
3128   /* Ditto, see above */
3129   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3130   memset(&si, 0, sizeof(si));
3131   si.cbSize = sizeof(si);
3132   si.fMask = SIF_PAGE | SIF_RANGE;
3133   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3134   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3135     "Vertical scrollbar is invisible, should be visible.\n");
3136   todo_wine {
3137   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3138         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3139         si.nPage, si.nMin, si.nMax);
3140   }
3141 
3142   /* Ditto, see above */
3143   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3144   memset(&si, 0, sizeof(si));
3145   si.cbSize = sizeof(si);
3146   si.fMask = SIF_PAGE | SIF_RANGE;
3147   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3148   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3149     "Vertical scrollbar is invisible, should be visible.\n");
3150   todo_wine {
3151   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3152         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3153         si.nPage, si.nMin, si.nMax);
3154   }
3155 
3156   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3157   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3158   memset(&si, 0, sizeof(si));
3159   si.cbSize = sizeof(si);
3160   si.fMask = SIF_PAGE | SIF_RANGE;
3161   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3162   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3163     "Vertical scrollbar is visible, should be invisible.\n");
3164   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3165         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3166         si.nPage, si.nMin, si.nMax);
3167 
3168   DestroyWindow(hwndRichEdit);
3169 
3170   hwndRichEdit = new_richedit(NULL);
3171 
3172   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3173   memset(&si, 0, sizeof(si));
3174   si.cbSize = sizeof(si);
3175   si.fMask = SIF_PAGE | SIF_RANGE;
3176   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3177   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3178     "Vertical scrollbar is visible, should be invisible.\n");
3179   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3180         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3181         si.nPage, si.nMin, si.nMax);
3182 
3183   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3184   memset(&si, 0, sizeof(si));
3185   si.cbSize = sizeof(si);
3186   si.fMask = SIF_PAGE | SIF_RANGE;
3187   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3188   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3189     "Vertical scrollbar is visible, should be invisible.\n");
3190   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3191         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3192         si.nPage, si.nMin, si.nMax);
3193 
3194   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3195   memset(&si, 0, sizeof(si));
3196   si.cbSize = sizeof(si);
3197   si.fMask = SIF_PAGE | SIF_RANGE;
3198   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3199   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3200     "Vertical scrollbar is visible, should be invisible.\n");
3201   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3202         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3203         si.nPage, si.nMin, si.nMax);
3204 
3205   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3206   memset(&si, 0, sizeof(si));
3207   si.cbSize = sizeof(si);
3208   si.fMask = SIF_PAGE | SIF_RANGE;
3209   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3210   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3211     "Vertical scrollbar is visible, should be invisible.\n");
3212   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3213         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3214         si.nPage, si.nMin, si.nMax);
3215 
3216   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3217   memset(&si, 0, sizeof(si));
3218   si.cbSize = sizeof(si);
3219   si.fMask = SIF_PAGE | SIF_RANGE;
3220   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3221   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3222     "Vertical scrollbar is invisible, should be visible.\n");
3223   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3224         "reported page/range is %d (%d..%d)\n",
3225         si.nPage, si.nMin, si.nMax);
3226 
3227   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3228   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3229   memset(&si, 0, sizeof(si));
3230   si.cbSize = sizeof(si);
3231   si.fMask = SIF_PAGE | SIF_RANGE;
3232   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3233   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3234     "Vertical scrollbar is visible, should be invisible.\n");
3235   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3236         "reported page/range is %d (%d..%d)\n",
3237         si.nPage, si.nMin, si.nMax);
3238 
3239   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3240   memset(&si, 0, sizeof(si));
3241   si.cbSize = sizeof(si);
3242   si.fMask = SIF_PAGE | SIF_RANGE;
3243   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3244   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3245     "Vertical scrollbar is visible, should be invisible.\n");
3246   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3247         "reported page/range is %d (%d..%d)\n",
3248         si.nPage, si.nMin, si.nMax);
3249 
3250   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3251      EM_SCROLL will make visible any forcefully invisible scrollbar */
3252   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3253   memset(&si, 0, sizeof(si));
3254   si.cbSize = sizeof(si);
3255   si.fMask = SIF_PAGE | SIF_RANGE;
3256   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3257   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3258     "Vertical scrollbar is invisible, should be visible.\n");
3259   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3260         "reported page/range is %d (%d..%d)\n",
3261         si.nPage, si.nMin, si.nMax);
3262 
3263   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3264   memset(&si, 0, sizeof(si));
3265   si.cbSize = sizeof(si);
3266   si.fMask = SIF_PAGE | SIF_RANGE;
3267   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3268   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3269     "Vertical scrollbar is visible, should be invisible.\n");
3270   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3271         "reported page/range is %d (%d..%d)\n",
3272         si.nPage, si.nMin, si.nMax);
3273 
3274   /* Again, EM_SCROLL, with SB_LINEUP */
3275   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3276   memset(&si, 0, sizeof(si));
3277   si.cbSize = sizeof(si);
3278   si.fMask = SIF_PAGE | SIF_RANGE;
3279   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3280   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3281     "Vertical scrollbar is invisible, should be visible.\n");
3282   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3283         "reported page/range is %d (%d..%d)\n",
3284         si.nPage, si.nMin, si.nMax);
3285 
3286   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3287   memset(&si, 0, sizeof(si));
3288   si.cbSize = sizeof(si);
3289   si.fMask = SIF_PAGE | SIF_RANGE;
3290   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3291   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3292     "Vertical scrollbar is visible, should be invisible.\n");
3293   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3294         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3295         si.nPage, si.nMin, si.nMax);
3296 
3297   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3298   memset(&si, 0, sizeof(si));
3299   si.cbSize = sizeof(si);
3300   si.fMask = SIF_PAGE | SIF_RANGE;
3301   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3302   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3303     "Vertical scrollbar is invisible, should be visible.\n");
3304   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3305         "reported page/range is %d (%d..%d)\n",
3306         si.nPage, si.nMin, si.nMax);
3307 
3308   DestroyWindow(hwndRichEdit);
3309 
3310 
3311   /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3312   hwndRichEdit = new_richedit(NULL);
3313 
3314 #define ENABLE_WS_VSCROLL(hwnd) \
3315     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3316 #define DISABLE_WS_VSCROLL(hwnd) \
3317     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3318 
3319   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3320   ENABLE_WS_VSCROLL(hwndRichEdit);
3321   memset(&si, 0, sizeof(si));
3322   si.cbSize = sizeof(si);
3323   si.fMask = SIF_PAGE | SIF_RANGE;
3324   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3325   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3326     "Vertical scrollbar is invisible, should be visible.\n");
3327   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3328         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3329         si.nPage, si.nMin, si.nMax);
3330 
3331   /* Ditto, see above */
3332   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3333   memset(&si, 0, sizeof(si));
3334   si.cbSize = sizeof(si);
3335   si.fMask = SIF_PAGE | SIF_RANGE;
3336   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3337   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3338     "Vertical scrollbar is invisible, should be visible.\n");
3339   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3340         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3341         si.nPage, si.nMin, si.nMax);
3342 
3343   /* Ditto, see above */
3344   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3345   memset(&si, 0, sizeof(si));
3346   si.cbSize = sizeof(si);
3347   si.fMask = SIF_PAGE | SIF_RANGE;
3348   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3349   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3350     "Vertical scrollbar is invisible, should be visible.\n");
3351   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3352         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3353         si.nPage, si.nMin, si.nMax);
3354 
3355   /* Ditto, see above */
3356   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3357   memset(&si, 0, sizeof(si));
3358   si.cbSize = sizeof(si);
3359   si.fMask = SIF_PAGE | SIF_RANGE;
3360   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3361   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3362     "Vertical scrollbar is invisible, should be visible.\n");
3363   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3364         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3365         si.nPage, si.nMin, si.nMax);
3366 
3367   /* Ditto, see above */
3368   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3369   memset(&si, 0, sizeof(si));
3370   si.cbSize = sizeof(si);
3371   si.fMask = SIF_PAGE | SIF_RANGE;
3372   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3373   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3374     "Vertical scrollbar is invisible, should be visible.\n");
3375   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3376         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3377         si.nPage, si.nMin, si.nMax);
3378 
3379   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3380   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3381   memset(&si, 0, sizeof(si));
3382   si.cbSize = sizeof(si);
3383   si.fMask = SIF_PAGE | SIF_RANGE;
3384   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3385   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3386     "Vertical scrollbar is visible, should be invisible.\n");
3387   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3388         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3389         si.nPage, si.nMin, si.nMax);
3390 
3391   DestroyWindow(hwndRichEdit);
3392 
3393   hwndRichEdit = new_richedit(NULL);
3394 
3395   DISABLE_WS_VSCROLL(hwndRichEdit);
3396   memset(&si, 0, sizeof(si));
3397   si.cbSize = sizeof(si);
3398   si.fMask = SIF_PAGE | SIF_RANGE;
3399   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3400   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3401     "Vertical scrollbar is visible, should be invisible.\n");
3402   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3403         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3404         si.nPage, si.nMin, si.nMax);
3405 
3406   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3407   memset(&si, 0, sizeof(si));
3408   si.cbSize = sizeof(si);
3409   si.fMask = SIF_PAGE | SIF_RANGE;
3410   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3411   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3412     "Vertical scrollbar is visible, should be invisible.\n");
3413   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3414         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3415         si.nPage, si.nMin, si.nMax);
3416 
3417   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3418   memset(&si, 0, sizeof(si));
3419   si.cbSize = sizeof(si);
3420   si.fMask = SIF_PAGE | SIF_RANGE;
3421   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3422   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3423     "Vertical scrollbar is visible, should be invisible.\n");
3424   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3425         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3426         si.nPage, si.nMin, si.nMax);
3427 
3428   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3429   memset(&si, 0, sizeof(si));
3430   si.cbSize = sizeof(si);
3431   si.fMask = SIF_PAGE | SIF_RANGE;
3432   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3433   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3434     "Vertical scrollbar is visible, should be invisible.\n");
3435   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3436         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3437         si.nPage, si.nMin, si.nMax);
3438 
3439   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3440   memset(&si, 0, sizeof(si));
3441   si.cbSize = sizeof(si);
3442   si.fMask = SIF_PAGE | SIF_RANGE;
3443   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3444   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3445     "Vertical scrollbar is invisible, should be visible.\n");
3446   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3447         "reported page/range is %d (%d..%d)\n",
3448         si.nPage, si.nMin, si.nMax);
3449 
3450   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3451   DISABLE_WS_VSCROLL(hwndRichEdit);
3452   memset(&si, 0, sizeof(si));
3453   si.cbSize = sizeof(si);
3454   si.fMask = SIF_PAGE | SIF_RANGE;
3455   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3456   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3457     "Vertical scrollbar is visible, should be invisible.\n");
3458   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3459         "reported page/range is %d (%d..%d)\n",
3460         si.nPage, si.nMin, si.nMax);
3461 
3462   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3463   memset(&si, 0, sizeof(si));
3464   si.cbSize = sizeof(si);
3465   si.fMask = SIF_PAGE | SIF_RANGE;
3466   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3467   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3468     "Vertical scrollbar is visible, should be invisible.\n");
3469   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3470         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3471         si.nPage, si.nMin, si.nMax);
3472 
3473   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3474   memset(&si, 0, sizeof(si));
3475   si.cbSize = sizeof(si);
3476   si.fMask = SIF_PAGE | SIF_RANGE;
3477   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3478   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3479     "Vertical scrollbar is invisible, should be visible.\n");
3480   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3481         "reported page/range is %d (%d..%d)\n",
3482         si.nPage, si.nMin, si.nMax);
3483 
3484   DISABLE_WS_VSCROLL(hwndRichEdit);
3485   memset(&si, 0, sizeof(si));
3486   si.cbSize = sizeof(si);
3487   si.fMask = SIF_PAGE | SIF_RANGE;
3488   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3489   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3490     "Vertical scrollbar is visible, should be invisible.\n");
3491   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3492         "reported page/range is %d (%d..%d)\n",
3493         si.nPage, si.nMin, si.nMax);
3494 
3495   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3496      EM_SCROLL will make visible any forcefully invisible scrollbar */
3497   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3498   memset(&si, 0, sizeof(si));
3499   si.cbSize = sizeof(si);
3500   si.fMask = SIF_PAGE | SIF_RANGE;
3501   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3502   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3503     "Vertical scrollbar is invisible, should be visible.\n");
3504   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3505         "reported page/range is %d (%d..%d)\n",
3506         si.nPage, si.nMin, si.nMax);
3507 
3508   DISABLE_WS_VSCROLL(hwndRichEdit);
3509   memset(&si, 0, sizeof(si));
3510   si.cbSize = sizeof(si);
3511   si.fMask = SIF_PAGE | SIF_RANGE;
3512   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3513   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3514     "Vertical scrollbar is visible, should be invisible.\n");
3515   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3516         "reported page/range is %d (%d..%d)\n",
3517         si.nPage, si.nMin, si.nMax);
3518 
3519   /* Again, EM_SCROLL, with SB_LINEUP */
3520   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3521   memset(&si, 0, sizeof(si));
3522   si.cbSize = sizeof(si);
3523   si.fMask = SIF_PAGE | SIF_RANGE;
3524   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3525   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3526     "Vertical scrollbar is invisible, should be visible.\n");
3527   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3528         "reported page/range is %d (%d..%d)\n",
3529         si.nPage, si.nMin, si.nMax);
3530 
3531   DestroyWindow(hwndRichEdit);
3532 
3533   /* This window proc models what is going on with Corman Lisp 3.0.
3534      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3535      force the scrollbar into visibility. Recursion should NOT happen
3536      as a result of this action.
3537    */
3538   r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3539   if (r) {
3540     richeditProc = cls.lpfnWndProc;
3541     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3542     cls.lpszClassName = "RicheditStupidOverride";
3543     if(!RegisterClassA(&cls)) assert(0);
3544 
3545     recursionLevel = 0;
3546     WM_SIZE_recursionLevel = 0;
3547     bailedOutOfRecursion = FALSE;
3548     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3549     ok(!bailedOutOfRecursion,
3550         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3551 
3552     recursionLevel = 0;
3553     WM_SIZE_recursionLevel = 0;
3554     bailedOutOfRecursion = FALSE;
3555     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3556     ok(!bailedOutOfRecursion,
3557         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3558 
3559     /* Unblock window in order to process WM_DESTROY */
3560     recursionLevel = 0;
3561     bailedOutOfRecursion = FALSE;
3562     WM_SIZE_recursionLevel = 0;
3563     DestroyWindow(hwndRichEdit);
3564   }
3565 }
3566 
3567 static void test_EM_SETUNDOLIMIT(void)
3568 {
3569   /* cases we test for:
3570    * default behaviour - limiting at 100 undo's
3571    * undo disabled - setting a limit of 0
3572    * undo limited -  undo limit set to some to some number, like 2
3573    * bad input - sending a negative number should default to 100 undo's */
3574 
3575   HWND hwndRichEdit = new_richedit(NULL);
3576   CHARRANGE cr;
3577   int i;
3578   int result;
3579 
3580   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3581   cr.cpMin = 0;
3582   cr.cpMax = -1;
3583   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3584 
3585   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3586     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3587       also, multiple pastes don't combine like WM_CHAR would */
3588 
3589   /* first case - check the default */
3590   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3591   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3592     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3593   for (i=0; i<100; i++) /* Undo 100 of them */
3594     SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3595   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3596      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3597 
3598   /* second case - cannot undo */
3599   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3600   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3601   SendMessageA(hwndRichEdit,
3602               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3603   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3604      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3605 
3606   /* third case - set it to an arbitrary number */
3607   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3608   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3609   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3610   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3611   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3612   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3613   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3614      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3615   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3616   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3617      "EM_SETUNDOLIMIT didn't allow a second 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 allowed a third undo with UNDOLIMIT set to 2\n");
3621 
3622   /* fourth case - setting negative numbers should default to 100 undos */
3623   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3624   result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3625   ok (result == 100,
3626       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3627 
3628   DestroyWindow(hwndRichEdit);
3629 }
3630 
3631 static void test_ES_PASSWORD(void)
3632 {
3633   /* This isn't hugely testable, so we're just going to run it through its paces */
3634 
3635   HWND hwndRichEdit = new_richedit(NULL);
3636   WCHAR result;
3637 
3638   /* First, check the default of a regular control */
3639   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3640   ok (result == 0,
3641 	"EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3642 
3643   /* Now, set it to something normal */
3644   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3645   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3646   ok (result == 120,
3647 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3648 
3649   /* Now, set it to something odd */
3650   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3651   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3652   ok (result == 1234,
3653 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3654   DestroyWindow(hwndRichEdit);
3655 }
3656 
3657 LONG streamout_written = 0;
3658 
3659 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3660                                          LPBYTE pbBuff,
3661                                          LONG cb,
3662                                          LONG *pcb)
3663 {
3664   char** str = (char**)dwCookie;
3665   *pcb = cb;
3666   if (*pcb > 0) {
3667     memcpy(*str, pbBuff, *pcb);
3668     *str += *pcb;
3669   }
3670   streamout_written = *pcb;
3671   return 0;
3672 }
3673 
3674 static void test_WM_SETTEXT(void)
3675 {
3676   HWND hwndRichEdit = new_richedit(NULL);
3677   const char * TestItem1 = "TestSomeText";
3678   const char * TestItem2 = "TestSomeText\r";
3679   const char * TestItem2_after = "TestSomeText\r\n";
3680   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3681   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3682   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3683   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3684   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3685   const char * TestItem5_after = "TestSomeText TestSomeText";
3686   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3687   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3688   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3689   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3690 
3691   const char rtftextA[] = "{\\rtf sometext}";
3692   const char urtftextA[] = "{\\urtf sometext}";
3693   const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3694   const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3695   const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3696 
3697   char buf[1024] = {0};
3698   WCHAR bufW[1024] = {0};
3699   LRESULT result;
3700 
3701   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3702      any solitary \r to be converted to \r\n on return. Properly paired
3703      \r\n are not affected. It also shows that the special sequence \r\r\n
3704      gets converted to a single space.
3705    */
3706 
3707 #define TEST_SETTEXT(a, b) \
3708   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3709   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3710   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3711   ok (result == lstrlenA(buf), \
3712 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3713 	result, lstrlenA(buf)); \
3714   result = strcmp(b, buf); \
3715   ok(result == 0, \
3716         "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3717 
3718   TEST_SETTEXT(TestItem1, TestItem1)
3719   TEST_SETTEXT(TestItem2, TestItem2_after)
3720   TEST_SETTEXT(TestItem3, TestItem3_after)
3721   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3722   TEST_SETTEXT(TestItem4, TestItem4_after)
3723   TEST_SETTEXT(TestItem5, TestItem5_after)
3724   TEST_SETTEXT(TestItem6, TestItem6_after)
3725   TEST_SETTEXT(TestItem7, TestItem7_after)
3726 
3727   /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3728   TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3729   TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3730   TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3731   TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3732   DestroyWindow(hwndRichEdit);
3733 #undef TEST_SETTEXT
3734 
3735 #define TEST_SETTEXTW(a, b) \
3736   result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3737   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3738   result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3739   ok (result == lstrlenW(bufW), \
3740 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3741 	result, lstrlenW(bufW)); \
3742   result = lstrcmpW(b, bufW); \
3743   ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3744 
3745   hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3746                                ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3747                                0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3748   ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3749   TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3750   TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3751   TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3752   TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3753   DestroyWindow(hwndRichEdit);
3754 #undef TEST_SETTEXTW
3755 
3756   /* Single-line richedit */
3757   hwndRichEdit = new_richedit_with_style(NULL, 0);
3758   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3759   ok(result == 1, "WM_SETTEXT returned %ld, expected 12\n", result);
3760   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3761   ok(result == 5, "WM_GETTEXT returned %ld, expected 5\n", result);
3762   ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3763   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3764   ok(result == 1, "WM_SETTEXT returned %ld, expected 1\n", result);
3765   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3766   ok(result == 3, "WM_GETTEXT returned %ld, expected 3\n", result);
3767   ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3768   DestroyWindow(hwndRichEdit);
3769 }
3770 
3771 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3772    resent to the callkack. */
3773 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3774                                                 LPBYTE pbBuff,
3775                                                 LONG cb,
3776                                                 LONG *pcb)
3777 {
3778   char** str = (char**)dwCookie;
3779   ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3780   *pcb = 0;
3781   if (cb > 0) {
3782     memcpy(*str, pbBuff, cb);
3783     *str += cb;
3784     *pcb = 1;
3785   }
3786   return 0;
3787 }
3788 
3789 static int count_pars(const char *buf)
3790 {
3791     const char *p = buf;
3792     int count = 0;
3793     while ((p = strstr( p, "\\par" )) != NULL)
3794     {
3795         if (!isalpha( p[4] ))
3796            count++;
3797         p++;
3798     }
3799     return count;
3800 }
3801 
3802 static void test_EM_STREAMOUT(void)
3803 {
3804   HWND hwndRichEdit = new_richedit(NULL);
3805   int r;
3806   EDITSTREAM es;
3807   char buf[1024] = {0};
3808   char * p;
3809   LRESULT result;
3810 
3811   const char * TestItem1 = "TestSomeText";
3812   const char * TestItem2 = "TestSomeText\r";
3813   const char * TestItem3 = "TestSomeText\r\n";
3814 
3815   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3816   p = buf;
3817   es.dwCookie = (DWORD_PTR)&p;
3818   es.dwError = 0;
3819   es.pfnCallback = test_WM_SETTEXT_esCallback;
3820   memset(buf, 0, sizeof(buf));
3821   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3822   r = strlen(buf);
3823   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3824   ok(strcmp(buf, TestItem1) == 0,
3825         "streamed text different, got %s\n", buf);
3826   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3827 
3828   /* RTF mode writes the final end of para \r if it's part of the selection */
3829   p = buf;
3830   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3831   ok (count_pars(buf) == 1, "got %s\n", buf);
3832   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3833   p = buf;
3834   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3835   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3836   ok (count_pars(buf) == 0, "got %s\n", buf);
3837   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3838   p = buf;
3839   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3840   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3841   ok (count_pars(buf) == 1, "got %s\n", buf);
3842   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3843 
3844   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3845   p = buf;
3846   es.dwCookie = (DWORD_PTR)&p;
3847   es.dwError = 0;
3848   es.pfnCallback = test_WM_SETTEXT_esCallback;
3849   memset(buf, 0, sizeof(buf));
3850   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3851   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3852   r = strlen(buf);
3853   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3854   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3855   ok(strcmp(buf, TestItem3) == 0,
3856         "streamed text different from, got %s\n", buf);
3857 
3858   /* And again RTF mode writes the final end of para \r if it's part of the selection */
3859   p = buf;
3860   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3861   ok (count_pars(buf) == 2, "got %s\n", buf);
3862   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3863   p = buf;
3864   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3865   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3866   ok (count_pars(buf) == 1, "got %s\n", buf);
3867   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3868   p = buf;
3869   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3870   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3871   ok (count_pars(buf) == 2, "got %s\n", buf);
3872   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3873 
3874   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3875   p = buf;
3876   es.dwCookie = (DWORD_PTR)&p;
3877   es.dwError = 0;
3878   es.pfnCallback = test_WM_SETTEXT_esCallback;
3879   memset(buf, 0, sizeof(buf));
3880   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3881   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3882   r = strlen(buf);
3883   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3884   ok(strcmp(buf, TestItem3) == 0,
3885         "streamed text different, got %s\n", buf);
3886 
3887   /* Use a callback that sets *pcb to one */
3888   p = buf;
3889   es.dwCookie = (DWORD_PTR)&p;
3890   es.dwError = 0;
3891   es.pfnCallback = test_esCallback_written_1;
3892   memset(buf, 0, sizeof(buf));
3893   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3894   r = strlen(buf);
3895   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3896   ok(strcmp(buf, TestItem3) == 0,
3897         "streamed text different, got %s\n", buf);
3898   ok(result == 0, "got %ld expected 0\n", result);
3899 
3900 
3901   DestroyWindow(hwndRichEdit);
3902 }
3903 
3904 static void test_EM_STREAMOUT_FONTTBL(void)
3905 {
3906   HWND hwndRichEdit = new_richedit(NULL);
3907   EDITSTREAM es;
3908   char buf[1024] = {0};
3909   char * p;
3910   char * fontTbl;
3911   int brackCount;
3912 
3913   const char * TestItem = "TestSomeText";
3914 
3915   /* fills in the richedit control with some text */
3916   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3917 
3918   /* streams out the text in rtf format */
3919   p = buf;
3920   es.dwCookie = (DWORD_PTR)&p;
3921   es.dwError = 0;
3922   es.pfnCallback = test_WM_SETTEXT_esCallback;
3923   memset(buf, 0, sizeof(buf));
3924   SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3925 
3926   /* scans for \fonttbl, error if not found */
3927   fontTbl = strstr(buf, "\\fonttbl");
3928   ok(fontTbl != NULL, "missing \\fonttbl section\n");
3929   if(fontTbl)
3930   {
3931       /* scans for terminating closing bracket */
3932       brackCount = 1;
3933       while(*fontTbl && brackCount)
3934       {
3935           if(*fontTbl == '{')
3936               brackCount++;
3937           else if(*fontTbl == '}')
3938               brackCount--;
3939           fontTbl++;
3940       }
3941     /* checks whether closing bracket is ok */
3942       ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3943       if(!brackCount)
3944       {
3945           /* char before closing fonttbl block should be a closed bracket */
3946           fontTbl -= 2;
3947           ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3948 
3949           /* char after fonttbl block should be a crlf */
3950           fontTbl += 2;
3951           ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3952       }
3953   }
3954   DestroyWindow(hwndRichEdit);
3955 }
3956 
3957 static void test_EM_STREAMOUT_empty_para(void)
3958 {
3959     HWND hwnd = new_richedit(NULL);
3960     char buf[1024], *p = buf;
3961     EDITSTREAM es;
3962 
3963     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
3964 
3965     memset(buf, 0, sizeof(buf));
3966     es.dwCookie    = (DWORD_PTR)&p;
3967     es.dwError     = 0;
3968     es.pfnCallback = test_WM_SETTEXT_esCallback;
3969 
3970     SendMessageA(hwnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3971     ok((p = strstr(buf, "\\pard")) != NULL, "missing \\pard\n");
3972     ok(((p = strstr(p, "\\fs")) && isdigit(p[3])), "missing \\fs\n");
3973 
3974     DestroyWindow(hwnd);
3975 }
3976 
3977 static void test_EM_SETTEXTEX(void)
3978 {
3979   HWND hwndRichEdit, parent;
3980   SCROLLINFO si;
3981   int sel_start, sel_end;
3982   SETTEXTEX setText;
3983   GETTEXTEX getText;
3984   WCHAR TestItem1[] = {'T', 'e', 's', 't',
3985                        'S', 'o', 'm', 'e',
3986                        'T', 'e', 'x', 't', 0};
3987   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3988                           't', 'S', 'o', 'm',
3989                           'e', 'T', 'e', 'x',
3990                           't', 't', 'S', 'o',
3991                           'm', 'e', 'T', 'e',
3992                           'x', 't', 0};
3993   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3994                            '\r','t','S','o','m','e','T','e','x','t',0};
3995   WCHAR TestItem2[] = {'T', 'e', 's', 't',
3996                        'S', 'o', 'm', 'e',
3997                        'T', 'e', 'x', 't',
3998                       '\r', 0};
3999   const char * TestItem2_after = "TestSomeText\r\n";
4000   WCHAR TestItem3[] = {'T', 'e', 's', 't',
4001                        'S', 'o', 'm', 'e',
4002                        'T', 'e', 'x', 't',
4003                       '\r','\n','\r','\n', 0};
4004   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
4005                        'S', 'o', 'm', 'e',
4006                        'T', 'e', 'x', 't',
4007                        '\n','\n', 0};
4008   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
4009                        'S', 'o', 'm', 'e',
4010                        'T', 'e', 'x', 't',
4011                        '\r','\r', 0};
4012   WCHAR TestItem4[] = {'T', 'e', 's', 't',
4013                        'S', 'o', 'm', 'e',
4014                        'T', 'e', 'x', 't',
4015                       '\r','\r','\n','\r',
4016                       '\n', 0};
4017   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
4018                        'S', 'o', 'm', 'e',
4019                        'T', 'e', 'x', 't',
4020                        ' ','\r', 0};
4021 #define MAX_BUF_LEN 1024
4022   WCHAR buf[MAX_BUF_LEN];
4023   char bufACP[MAX_BUF_LEN];
4024   char * p;
4025   int result;
4026   CHARRANGE cr;
4027   EDITSTREAM es;
4028   WNDCLASSA cls;
4029 
4030   /* Test the scroll position with and without a parent window.
4031    *
4032    * For some reason the scroll position is 0 after EM_SETTEXTEX
4033    * with the ST_SELECTION flag only when the control has a parent
4034    * window, even though the selection is at the end. */
4035   cls.style = 0;
4036   cls.lpfnWndProc = DefWindowProcA;
4037   cls.cbClsExtra = 0;
4038   cls.cbWndExtra = 0;
4039   cls.hInstance = GetModuleHandleA(0);
4040   cls.hIcon = 0;
4041   cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
4042   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4043   cls.lpszMenuName = NULL;
4044   cls.lpszClassName = "ParentTestClass";
4045   if(!RegisterClassA(&cls)) assert(0);
4046 
4047   parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4048                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
4049   ok (parent != 0, "Failed to create parent window\n");
4050 
4051   hwndRichEdit = CreateWindowExA(0,
4052                         RICHEDIT_CLASS20A, NULL,
4053                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4054                         0, 0, 200, 60, parent, NULL,
4055                         hmoduleRichEdit, NULL);
4056 
4057   setText.codepage = CP_ACP;
4058   setText.flags = ST_SELECTION;
4059   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4060                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4061   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4062   si.cbSize = sizeof(si);
4063   si.fMask = SIF_ALL;
4064   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4065   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4066   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4067   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4068   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4069 
4070   DestroyWindow(parent);
4071 
4072   /* Test without a parent window */
4073   hwndRichEdit = new_richedit(NULL);
4074   setText.codepage = CP_ACP;
4075   setText.flags = ST_SELECTION;
4076   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4077                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4078   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4079   si.cbSize = sizeof(si);
4080   si.fMask = SIF_ALL;
4081   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4082   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4083   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4084   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4085   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4086 
4087   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4088    * but this time it is because the selection is at the beginning. */
4089   setText.codepage = CP_ACP;
4090   setText.flags = ST_DEFAULT;
4091   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4092                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4093   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4094   si.cbSize = sizeof(si);
4095   si.fMask = SIF_ALL;
4096   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4097   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4098   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4099   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4100   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4101 
4102   setText.codepage = 1200;  /* no constant for unicode */
4103   getText.codepage = 1200;  /* no constant for unicode */
4104   getText.cb = MAX_BUF_LEN;
4105   getText.flags = GT_DEFAULT;
4106   getText.lpDefaultChar = NULL;
4107   getText.lpUsedDefChar = NULL;
4108 
4109   setText.flags = 0;
4110   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4111   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4112   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4113   ok(lstrcmpW(buf, TestItem1) == 0,
4114       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4115 
4116   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4117      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4118    */
4119   setText.codepage = 1200;  /* no constant for unicode */
4120   getText.codepage = 1200;  /* no constant for unicode */
4121   getText.cb = MAX_BUF_LEN;
4122   getText.flags = GT_DEFAULT;
4123   getText.lpDefaultChar = NULL;
4124   getText.lpUsedDefChar = NULL;
4125   setText.flags = 0;
4126   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4127   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4128   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4129   ok(lstrcmpW(buf, TestItem2) == 0,
4130       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4131 
4132   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4133   SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4134   ok(strcmp((const char *)buf, TestItem2_after) == 0,
4135       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4136 
4137   /* Baseline test for just-enough buffer space for string */
4138   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4139   getText.codepage = 1200;  /* no constant for unicode */
4140   getText.flags = GT_DEFAULT;
4141   getText.lpDefaultChar = NULL;
4142   getText.lpUsedDefChar = NULL;
4143   memset(buf, 0, sizeof(buf));
4144   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4145   ok(lstrcmpW(buf, TestItem2) == 0,
4146       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4147 
4148   /* When there is enough space for one character, but not both, of the CRLF
4149      pair at the end of the string, the CR is not copied at all. That is,
4150      the caller must not see CRLF pairs truncated to CR at the end of the
4151      string.
4152    */
4153   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4154   getText.codepage = 1200;  /* no constant for unicode */
4155   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
4156   getText.lpDefaultChar = NULL;
4157   getText.lpUsedDefChar = NULL;
4158   memset(buf, 0, sizeof(buf));
4159   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4160   ok(lstrcmpW(buf, TestItem1) == 0,
4161       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4162 
4163 
4164   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4165   setText.codepage = 1200;  /* no constant for unicode */
4166   getText.codepage = 1200;  /* no constant for unicode */
4167   getText.cb = MAX_BUF_LEN;
4168   getText.flags = GT_DEFAULT;
4169   getText.lpDefaultChar = NULL;
4170   getText.lpUsedDefChar = NULL;
4171   setText.flags = 0;
4172   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4173   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4174   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4175   ok(lstrcmpW(buf, TestItem3_after) == 0,
4176       "EM_SETTEXTEX did not convert properly\n");
4177 
4178   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4179   setText.codepage = 1200;  /* no constant for unicode */
4180   getText.codepage = 1200;  /* no constant for unicode */
4181   getText.cb = MAX_BUF_LEN;
4182   getText.flags = GT_DEFAULT;
4183   getText.lpDefaultChar = NULL;
4184   getText.lpUsedDefChar = NULL;
4185   setText.flags = 0;
4186   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4187   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4188   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4189   ok(lstrcmpW(buf, TestItem3_after) == 0,
4190       "EM_SETTEXTEX did not convert properly\n");
4191 
4192   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4193   setText.codepage = 1200;  /* no constant for unicode */
4194   getText.codepage = 1200;  /* no constant for unicode */
4195   getText.cb = MAX_BUF_LEN;
4196   getText.flags = GT_DEFAULT;
4197   getText.lpDefaultChar = NULL;
4198   getText.lpUsedDefChar = NULL;
4199   setText.flags = 0;
4200   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4201   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4202   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4203   ok(lstrcmpW(buf, TestItem4_after) == 0,
4204       "EM_SETTEXTEX did not convert properly\n");
4205 
4206   /* !ST_SELECTION && Unicode && !\rtf */
4207   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4208   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4209 
4210   ok (result == 1,
4211       "EM_SETTEXTEX returned %d, instead of 1\n",result);
4212   ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4213 
4214   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4215   setText.flags = 0;
4216   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4217   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4218   /* select some text */
4219   cr.cpMax = 1;
4220   cr.cpMin = 3;
4221   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4222   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4223   setText.flags = ST_SELECTION;
4224   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4225   ok(result == 0,
4226       "EM_SETTEXTEX with NULL lParam to replace selection"
4227       " with no text should return 0. Got %i\n",
4228       result);
4229 
4230   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4231   setText.flags = 0;
4232   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4233   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4234   /* select some text */
4235   cr.cpMax = 1;
4236   cr.cpMin = 3;
4237   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4238   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4239   setText.flags = ST_SELECTION;
4240   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4241   /* get text */
4242   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4243   ok(result == lstrlenW(TestItem1),
4244       "EM_SETTEXTEX with NULL lParam to replace selection"
4245       " with no text should return 0. Got %i\n",
4246       result);
4247   ok(lstrlenW(buf) == 22,
4248       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4249       lstrlenW(buf) );
4250 
4251   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4252   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4253   p = (char *)buf;
4254   es.dwCookie = (DWORD_PTR)&p;
4255   es.dwError = 0;
4256   es.pfnCallback = test_WM_SETTEXT_esCallback;
4257   memset(buf, 0, sizeof(buf));
4258   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4259               (WPARAM)(SF_RTF), (LPARAM)&es);
4260   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4261 
4262   /* !ST_SELECTION && !Unicode && \rtf */
4263   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4264   getText.codepage = 1200;  /* no constant for unicode */
4265   getText.cb = MAX_BUF_LEN;
4266   getText.flags = GT_DEFAULT;
4267   getText.lpDefaultChar = NULL;
4268   getText.lpUsedDefChar = NULL;
4269 
4270   setText.flags = 0;
4271   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4272   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4273   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4274   ok(lstrcmpW(buf, TestItem1) == 0,
4275       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4276 
4277   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4278    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4279   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4280   getText.codepage = CP_ACP;
4281   getText.cb = MAX_BUF_LEN;
4282   getText.flags = GT_DEFAULT;
4283   getText.lpDefaultChar = NULL;
4284   getText.lpUsedDefChar = NULL;
4285 
4286   setText.flags = ST_SELECTION;
4287   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4288   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4289   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4290   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4291   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4292 
4293   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4294   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4295   p = (char *)buf;
4296   es.dwCookie = (DWORD_PTR)&p;
4297   es.dwError = 0;
4298   es.pfnCallback = test_WM_SETTEXT_esCallback;
4299   memset(buf, 0, sizeof(buf));
4300   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4301               (WPARAM)(SF_RTF), (LPARAM)&es);
4302   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4303 
4304   /* select some text */
4305   cr.cpMax = 1;
4306   cr.cpMin = 3;
4307   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4308 
4309   /* ST_SELECTION && !Unicode && \rtf */
4310   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4311   getText.codepage = 1200;  /* no constant for unicode */
4312   getText.cb = MAX_BUF_LEN;
4313   getText.flags = GT_DEFAULT;
4314   getText.lpDefaultChar = NULL;
4315   getText.lpUsedDefChar = NULL;
4316 
4317   setText.flags = ST_SELECTION;
4318   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4319   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4320   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4321 
4322   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4323   setText.codepage = 1200;  /* no constant for unicode */
4324   getText.codepage = CP_ACP;
4325   getText.cb = MAX_BUF_LEN;
4326 
4327   setText.flags = 0;
4328   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4329   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4330 
4331   /* select some text */
4332   cr.cpMax = 1;
4333   cr.cpMin = 3;
4334   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4335 
4336   /* ST_SELECTION && !Unicode && !\rtf */
4337   setText.codepage = CP_ACP;
4338   getText.codepage = 1200;  /* no constant for unicode */
4339   getText.cb = MAX_BUF_LEN;
4340   getText.flags = GT_DEFAULT;
4341   getText.lpDefaultChar = NULL;
4342   getText.lpUsedDefChar = NULL;
4343 
4344   setText.flags = ST_SELECTION;
4345   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4346   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4347   ok(lstrcmpW(buf, TestItem1alt) == 0,
4348       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4349       " using ST_SELECTION and non-Unicode\n");
4350 
4351   /* Test setting text using rich text format */
4352   setText.flags = 0;
4353   setText.codepage = CP_ACP;
4354   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4355   getText.codepage = CP_ACP;
4356   getText.cb = MAX_BUF_LEN;
4357   getText.flags = GT_DEFAULT;
4358   getText.lpDefaultChar = NULL;
4359   getText.lpUsedDefChar = NULL;
4360   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4361   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4362 
4363   setText.flags = 0;
4364   setText.codepage = CP_ACP;
4365   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4366   getText.codepage = CP_ACP;
4367   getText.cb = MAX_BUF_LEN;
4368   getText.flags = GT_DEFAULT;
4369   getText.lpDefaultChar = NULL;
4370   getText.lpUsedDefChar = NULL;
4371   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4372   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4373 
4374   /* test for utf8 text with BOM */
4375   setText.flags = 0;
4376   setText.codepage = CP_ACP;
4377   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4378   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4379   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4380   result = strcmp(bufACP, "TestUTF8WithBOM");
4381   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4382 
4383   setText.flags = 0;
4384   setText.codepage = CP_UTF8;
4385   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4386   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4387   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4388   result = strcmp(bufACP, "TestUTF8WithBOM");
4389   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4390 
4391   /* Test multibyte character */
4392   if (!is_lang_japanese)
4393     skip("Skip multibyte character tests on non-Japanese platform\n");
4394   else
4395   {
4396     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4397     setText.flags = ST_SELECTION;
4398     setText.codepage = CP_ACP;
4399     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4400     todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4401     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4402     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4403     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4404        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4405 
4406     setText.flags = ST_DEFAULT;
4407     setText.codepage = CP_ACP;
4408     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4409     ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4410     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4411     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4412     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4413        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4414 
4415     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4416     setText.flags = ST_SELECTION;
4417     setText.codepage = CP_ACP;
4418     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4419     todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4420     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4421     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4422     todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4423                  "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4424   }
4425 
4426   DestroyWindow(hwndRichEdit);
4427 
4428   /* Single-line richedit */
4429   hwndRichEdit = new_richedit_with_style(NULL, 0);
4430   setText.flags = ST_DEFAULT;
4431   setText.codepage = CP_ACP;
4432   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"line1\r\nline2");
4433   ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4434   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4435   ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4436   ok(!strcmp(bufACP, "line1"), "EM_SETTEXTEX: Test single-line text: Result: %s\n", bufACP);
4437   DestroyWindow(hwndRichEdit);
4438 }
4439 
4440 static void test_EM_LIMITTEXT(void)
4441 {
4442   int ret;
4443 
4444   HWND hwndRichEdit = new_richedit(NULL);
4445 
4446   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4447    * about setting the length to -1 for multiline edit controls doesn't happen.
4448    */
4449 
4450   /* Don't check default gettextlimit case. That's done in other tests */
4451 
4452   /* Set textlimit to 100 */
4453   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4454   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4455   ok (ret == 100,
4456       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4457 
4458   /* Set textlimit to 0 */
4459   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4460   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4461   ok (ret == 65536,
4462       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4463 
4464   /* Set textlimit to -1 */
4465   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4466   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4467   ok (ret == -1,
4468       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4469 
4470   /* Set textlimit to -2 */
4471   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4472   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4473   ok (ret == -2,
4474       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4475 
4476   DestroyWindow (hwndRichEdit);
4477 }
4478 
4479 
4480 static void test_EM_EXLIMITTEXT(void)
4481 {
4482   int i, selBegin, selEnd, len1, len2;
4483   int result;
4484   char text[1024 + 1];
4485   char buffer[1024 + 1];
4486   int textlimit = 0; /* multiple of 100 */
4487   HWND hwndRichEdit = new_richedit(NULL);
4488 
4489   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4490   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4491 
4492   textlimit = 256000;
4493   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4494   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4495   /* set higher */
4496   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4497 
4498   textlimit = 1000;
4499   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4500   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4501   /* set lower */
4502   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4503 
4504   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4505   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4506   /* default for WParam = 0 */
4507   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4508 
4509   textlimit = sizeof(text)-1;
4510   memset(text, 'W', textlimit);
4511   text[sizeof(text)-1] = 0;
4512   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4513   /* maxed out text */
4514   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4515 
4516   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4517   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4518   len1 = selEnd - selBegin;
4519 
4520   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4521   SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4522   SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4523   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4524   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4525   len2 = selEnd - selBegin;
4526 
4527   ok(len1 != len2,
4528     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4529     len1,len2,i);
4530 
4531   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4532   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4533   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4534   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4535   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4536   len1 = selEnd - selBegin;
4537 
4538   ok(len1 != len2,
4539     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4540     len1,len2,i);
4541 
4542   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4543   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4544   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
4545   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4546   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4547   len2 = selEnd - selBegin;
4548 
4549   ok(len1 == len2,
4550     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4551     len1,len2,i);
4552 
4553   /* set text up to the limit, select all the text, then add a char */
4554   textlimit = 5;
4555   memset(text, 'W', textlimit);
4556   text[textlimit] = 0;
4557   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4558   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4559   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4560   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4561   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4562   result = strcmp(buffer, "A");
4563   ok(0 == result, "got string = \"%s\"\n", buffer);
4564 
4565   /* WM_SETTEXT not limited */
4566   textlimit = 10;
4567   memset(text, 'W', textlimit);
4568   text[textlimit] = 0;
4569   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4570   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4571   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4572   i = strlen(buffer);
4573   ok(10 == i, "expected 10 chars\n");
4574   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4575   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4576 
4577   /* try inserting more text at end */
4578   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4579   ok(0 == i, "WM_CHAR wasn't processed\n");
4580   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4581   i = strlen(buffer);
4582   ok(10 == i, "expected 10 chars, got %i\n", i);
4583   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4584   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4585 
4586   /* try inserting text at beginning */
4587   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4588   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4589   ok(0 == i, "WM_CHAR wasn't processed\n");
4590   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4591   i = strlen(buffer);
4592   ok(10 == i, "expected 10 chars, got %i\n", i);
4593   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4594   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4595 
4596   /* WM_CHAR is limited */
4597   textlimit = 1;
4598   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4599   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4600   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4601   ok(0 == i, "WM_CHAR wasn't processed\n");
4602   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4603   ok(0 == i, "WM_CHAR wasn't processed\n");
4604   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4605   i = strlen(buffer);
4606   ok(1 == i, "expected 1 chars, got %i instead\n", i);
4607 
4608   DestroyWindow(hwndRichEdit);
4609 }
4610 
4611 static void test_EM_GETLIMITTEXT(void)
4612 {
4613   int i;
4614   HWND hwndRichEdit = new_richedit(NULL);
4615 
4616   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4617   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4618 
4619   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4620   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4621   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4622 
4623   DestroyWindow(hwndRichEdit);
4624 }
4625 
4626 static void test_WM_SETFONT(void)
4627 {
4628   /* There is no invalid input or error conditions for this function.
4629    * NULL wParam and lParam just fall back to their default values
4630    * It should be noted that even if you use a gibberish name for your fonts
4631    * here, it will still work because the name is stored. They will display as
4632    * System, but will report their name to be whatever they were created as */
4633 
4634   HWND hwndRichEdit = new_richedit(NULL);
4635   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4636     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4637     FF_DONTCARE, "Marlett");
4638   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4639     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4640     FF_DONTCARE, "MS Sans Serif");
4641   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4642     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4643     FF_DONTCARE, "Courier");
4644   LOGFONTA sentLogFont;
4645   CHARFORMAT2A returnedCF2A;
4646 
4647   returnedCF2A.cbSize = sizeof(returnedCF2A);
4648 
4649   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4650   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4651   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4652 
4653   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4654   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4655     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4656     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4657 
4658   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4659   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4660   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4661   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4662     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4663     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4664 
4665   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4666   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4667   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4668   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4669     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4670     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4671 
4672   /* This last test is special since we send in NULL. We clear the variables
4673    * and just compare to "System" instead of the sent in font name. */
4674   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4675   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4676   returnedCF2A.cbSize = sizeof(returnedCF2A);
4677 
4678   SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4679   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4680   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4681   ok (!strcmp("System",returnedCF2A.szFaceName),
4682     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4683 
4684   DestroyWindow(hwndRichEdit);
4685 }
4686 
4687 
4688 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4689                                          LPBYTE pbBuff,
4690                                          LONG cb,
4691                                          LONG *pcb)
4692 {
4693   const char** str = (const char**)dwCookie;
4694   int size = strlen(*str);
4695   if(size > 3)  /* let's make it piecemeal for fun */
4696     size = 3;
4697   *pcb = cb;
4698   if (*pcb > size) {
4699     *pcb = size;
4700   }
4701   if (*pcb > 0) {
4702     memcpy(pbBuff, *str, *pcb);
4703     *str += *pcb;
4704   }
4705   return 0;
4706 }
4707 
4708 static void test_EM_GETMODIFY(void)
4709 {
4710   HWND hwndRichEdit = new_richedit(NULL);
4711   LRESULT result;
4712   SETTEXTEX setText;
4713   WCHAR TestItem1[] = {'T', 'e', 's', 't',
4714                        'S', 'o', 'm', 'e',
4715                        'T', 'e', 'x', 't', 0};
4716   WCHAR TestItem2[] = {'T', 'e', 's', 't',
4717                        'S', 'o', 'm', 'e',
4718                        'O', 't', 'h', 'e', 'r',
4719                        'T', 'e', 'x', 't', 0};
4720   const char* streamText = "hello world";
4721   CHARFORMAT2A cf2;
4722   PARAFORMAT2 pf2;
4723   EDITSTREAM es;
4724 
4725   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4726     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4727     FF_DONTCARE, "Courier");
4728 
4729   setText.codepage = 1200;  /* no constant for unicode */
4730   setText.flags = ST_KEEPUNDO;
4731 
4732 
4733   /* modify flag shouldn't be set when richedit is first created */
4734   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4735   ok (result == 0,
4736       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4737 
4738   /* setting modify flag should actually set it */
4739   SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4740   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4741   ok (result != 0,
4742       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4743 
4744   /* clearing modify flag should actually clear it */
4745   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4746   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4747   ok (result == 0,
4748       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4749 
4750   /* setting font doesn't change modify flag */
4751   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4752   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4753   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4754   ok (result == 0,
4755       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4756 
4757   /* setting text should set modify flag */
4758   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4759   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4760   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4761   ok (result != 0,
4762       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4763 
4764   /* undo previous text doesn't reset modify flag */
4765   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4766   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4767   ok (result != 0,
4768       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4769 
4770   /* set text with no flag to keep undo stack should not set modify flag */
4771   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4772   setText.flags = 0;
4773   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4774   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4775   ok (result == 0,
4776       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4777 
4778   /* WM_SETTEXT doesn't modify */
4779   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4780   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4781   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4782   ok (result == 0,
4783       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4784 
4785   /* clear the text */
4786   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4787   SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4788   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4789   ok (result == 0,
4790       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4791 
4792   /* replace text */
4793   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4794   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4795   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4796   SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4797   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4798   ok (result != 0,
4799       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4800 
4801   /* copy/paste text 1 */
4802   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4803   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4804   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4805   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4806   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4807   ok (result != 0,
4808       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4809 
4810   /* copy/paste text 2 */
4811   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4812   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4813   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4814   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4815   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4816   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4817   ok (result != 0,
4818       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4819 
4820   /* press char */
4821   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4822   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4823   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4824   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4825   ok (result != 0,
4826       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4827 
4828   /* press del */
4829   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4830   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4831   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4832   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4833   ok (result != 0,
4834       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4835 
4836   /* set char format */
4837   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4838   cf2.cbSize = sizeof(CHARFORMAT2A);
4839   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4840   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4841   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4842   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4843   result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4844   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4845   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4846   ok (result != 0,
4847       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4848 
4849   /* set para format */
4850   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4851   pf2.cbSize = sizeof(PARAFORMAT2);
4852   SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4853   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4854   pf2.wAlignment = PFA_RIGHT;
4855   SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4856   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4857   ok (result == 0,
4858       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4859 
4860   /* EM_STREAM */
4861   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4862   es.dwCookie = (DWORD_PTR)&streamText;
4863   es.dwError = 0;
4864   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4865   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4866   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4867   ok (result != 0,
4868       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4869 
4870   DestroyWindow(hwndRichEdit);
4871 }
4872 
4873 struct exsetsel_s {
4874   LONG min;
4875   LONG max;
4876   LRESULT expected_retval;
4877   int expected_getsel_start;
4878   int expected_getsel_end;
4879   BOOL todo;
4880 };
4881 
4882 static const struct exsetsel_s exsetsel_tests[] = {
4883   /* sanity tests */
4884   {5, 10, 10, 5, 10 },
4885   {15, 17, 17, 15, 17 },
4886   /* test cpMax > strlen() */
4887   {0, 100, 18, 0, 18 },
4888   /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4889   {-1, 1, 17, 17, 17 },
4890   /* test cpMin == cpMax */
4891   {5, 5, 5, 5, 5 },
4892   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4893   {-1, 0, 5, 5, 5 },
4894   {-1, 17, 5, 5, 5 },
4895   {-1, 18, 5, 5, 5 },
4896   /* test cpMin < 0 && cpMax < 0 */
4897   {-1, -1, 17, 17, 17 },
4898   {-4, -5, 17, 17, 17 },
4899   /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4900   {0, -1, 18, 0, 18 },
4901   {17, -5, 18, 17, 18 },
4902   {18, -3, 17, 17, 17 },
4903   /* test if cpMin > cpMax */
4904   {15, 19, 18, 15, 18 },
4905   {19, 15, 18, 15, 18 },
4906   /* cpMin == strlen() && cpMax > cpMin */
4907   {17, 18, 18, 17, 18 },
4908   {17, 50, 18, 17, 18 },
4909 };
4910 
4911 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4912     CHARRANGE cr;
4913     LRESULT result;
4914     int start, end;
4915 
4916     cr.cpMin = setsel->min;
4917     cr.cpMax = setsel->max;
4918     result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4919 
4920     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4921 
4922     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4923 
4924     todo_wine_if (setsel->todo)
4925         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4926             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4927 }
4928 
4929 static void test_EM_EXSETSEL(void)
4930 {
4931     HWND hwndRichEdit = new_richedit(NULL);
4932     int i;
4933     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4934 
4935     /* sending some text to the window */
4936     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4937     /*                                                 01234567890123456*/
4938     /*                                                          10      */
4939 
4940     for (i = 0; i < num_tests; i++) {
4941         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4942     }
4943 
4944     if (!is_lang_japanese)
4945         skip("Skip multibyte character tests on non-Japanese platform\n");
4946     else
4947     {
4948         CHARRANGE cr;
4949         char bufA[MAX_BUF_LEN] = {0};
4950         LRESULT result;
4951 
4952         /* Test with multibyte character */
4953         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4954         /*                                                 012345     6  78901 */
4955         cr.cpMin = 4, cr.cpMax = 8;
4956         result =  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4957         ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4958         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4959         ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4960         SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4961         ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4962         ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4963     }
4964 
4965     DestroyWindow(hwndRichEdit);
4966 }
4967 
4968 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4969     LRESULT result;
4970     int start, end;
4971 
4972     result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4973 
4974     ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4975 
4976     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4977 
4978     todo_wine_if (setsel->todo)
4979         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4980             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4981 }
4982 
4983 static void test_EM_SETSEL(void)
4984 {
4985     char buffA[32] = {0};
4986     HWND hwndRichEdit = new_richedit(NULL);
4987     int i;
4988     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4989 
4990     /* sending some text to the window */
4991     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4992     /*                                                 01234567890123456*/
4993     /*                                                          10      */
4994 
4995     for (i = 0; i < num_tests; i++) {
4996         check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4997     }
4998 
4999     SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
5000     buffA[0] = 123;
5001     SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5002     ok(buffA[0] == 0, "selection text %s\n", buffA);
5003 
5004     if (!is_lang_japanese)
5005         skip("Skip multibyte character tests on non-Japanese platform\n");
5006     else
5007     {
5008         int sel_start, sel_end;
5009         LRESULT result;
5010 
5011         /* Test with multibyte character */
5012         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5013         /*                                                 012345     6  78901 */
5014         result =  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
5015         ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
5016         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
5017         ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5018         result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5019         ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
5020         ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
5021     }
5022 
5023     DestroyWindow(hwndRichEdit);
5024 }
5025 
5026 static void test_EM_REPLACESEL(int redraw)
5027 {
5028     HWND hwndRichEdit = new_richedit(NULL);
5029     char buffer[1024] = {0};
5030     int r;
5031     GETTEXTEX getText;
5032     CHARRANGE cr;
5033     CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
5034     CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
5035 
5036     /* sending some text to the window */
5037     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5038     /*                                                 01234567890123456*/
5039     /*                                                          10      */
5040 
5041     /* FIXME add more tests */
5042     SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
5043     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
5044     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
5045     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5046     r = strcmp(buffer, "testing");
5047     ok(0 == r, "expected %d, got %d\n", 0, r);
5048 
5049     DestroyWindow(hwndRichEdit);
5050 
5051     hwndRichEdit = new_richedit(NULL);
5052 
5053     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5054     SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5055 
5056     /* Test behavior with carriage returns and newlines */
5057     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5058     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5059     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5060     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5061     r = strcmp(buffer, "RichEdit1");
5062     ok(0 == r, "expected %d, got %d\n", 0, r);
5063     getText.cb = 1024;
5064     getText.codepage = CP_ACP;
5065     getText.flags = GT_DEFAULT;
5066     getText.lpDefaultChar = NULL;
5067     getText.lpUsedDefChar = NULL;
5068     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5069     ok(strcmp(buffer, "RichEdit1") == 0,
5070       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5071 
5072     /* Test number of lines reported after EM_REPLACESEL */
5073     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5074     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5075 
5076     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5077     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5078     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5079     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5080     r = strcmp(buffer, "RichEdit1\r\n");
5081     ok(0 == r, "expected %d, got %d\n", 0, r);
5082     getText.cb = 1024;
5083     getText.codepage = CP_ACP;
5084     getText.flags = GT_DEFAULT;
5085     getText.lpDefaultChar = NULL;
5086     getText.lpUsedDefChar = NULL;
5087     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5088     ok(strcmp(buffer, "RichEdit1\r") == 0,
5089       "EM_GETTEXTEX returned incorrect string\n");
5090 
5091     /* Test number of lines reported after EM_REPLACESEL */
5092     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5093     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5094 
5095     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5096     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5097     ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5098 
5099     /* Test number of lines reported after EM_REPLACESEL */
5100     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5101     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5102 
5103     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5104     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5105     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5106     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5107 
5108     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5109     r = strcmp(buffer, "RichEdit1\r\n");
5110     ok(0 == r, "expected %d, got %d\n", 0, r);
5111     getText.cb = 1024;
5112     getText.codepage = CP_ACP;
5113     getText.flags = GT_DEFAULT;
5114     getText.lpDefaultChar = NULL;
5115     getText.lpUsedDefChar = NULL;
5116     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5117     ok(strcmp(buffer, "RichEdit1\r") == 0,
5118       "EM_GETTEXTEX returned incorrect string\n");
5119 
5120     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5121     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5122     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5123     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5124 
5125     /* The following tests show that richedit should handle the special \r\r\n
5126        sequence by turning it into a single space on insertion. However,
5127        EM_REPLACESEL on WinXP returns the number of characters in the original
5128        string.
5129      */
5130 
5131     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5132     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5133     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5134     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5135     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5136     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5137     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5138 
5139     /* Test the actual string */
5140     getText.cb = 1024;
5141     getText.codepage = CP_ACP;
5142     getText.flags = GT_DEFAULT;
5143     getText.lpDefaultChar = NULL;
5144     getText.lpUsedDefChar = NULL;
5145     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5146     ok(strcmp(buffer, "\r\r") == 0,
5147       "EM_GETTEXTEX returned incorrect string\n");
5148 
5149     /* Test number of lines reported after EM_REPLACESEL */
5150     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5151     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5152 
5153     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5154     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5155     ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5156     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5157     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5158     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
5159     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
5160 
5161     /* Test the actual string */
5162     getText.cb = 1024;
5163     getText.codepage = CP_ACP;
5164     getText.flags = GT_DEFAULT;
5165     getText.lpDefaultChar = NULL;
5166     getText.lpUsedDefChar = NULL;
5167     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5168     ok(strcmp(buffer, " ") == 0,
5169       "EM_GETTEXTEX returned incorrect string\n");
5170 
5171     /* Test number of lines reported after EM_REPLACESEL */
5172     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5173     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5174 
5175     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5176     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5177     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5178     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5179     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5180     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5181     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5182 
5183     /* Test the actual string */
5184     getText.cb = 1024;
5185     getText.codepage = CP_ACP;
5186     getText.flags = GT_DEFAULT;
5187     getText.lpDefaultChar = NULL;
5188     getText.lpUsedDefChar = NULL;
5189     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5190     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5191       "EM_GETTEXTEX returned incorrect string\n");
5192 
5193     /* Test number of lines reported after EM_REPLACESEL */
5194     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5195     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5196 
5197     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5198     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5199     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5200     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5201     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5202     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5203     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5204 
5205     /* Test the actual string */
5206     getText.cb = 1024;
5207     getText.codepage = CP_ACP;
5208     getText.flags = GT_DEFAULT;
5209     getText.lpDefaultChar = NULL;
5210     getText.lpUsedDefChar = NULL;
5211     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5212     ok(strcmp(buffer, " \r") == 0,
5213       "EM_GETTEXTEX returned incorrect string\n");
5214 
5215     /* Test number of lines reported after EM_REPLACESEL */
5216     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5217     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5218 
5219     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5220     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5221     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5222     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5223     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5224     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
5225     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
5226 
5227     /* Test the actual string */
5228     getText.cb = 1024;
5229     getText.codepage = CP_ACP;
5230     getText.flags = GT_DEFAULT;
5231     getText.lpDefaultChar = NULL;
5232     getText.lpUsedDefChar = NULL;
5233     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5234     ok(strcmp(buffer, " \r\r") == 0,
5235       "EM_GETTEXTEX returned incorrect string\n");
5236 
5237     /* Test number of lines reported after EM_REPLACESEL */
5238     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5239     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5240 
5241     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5242     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5243     ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5244     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5245     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5246     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
5247     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
5248 
5249     /* Test the actual string */
5250     getText.cb = 1024;
5251     getText.codepage = CP_ACP;
5252     getText.flags = GT_DEFAULT;
5253     getText.lpDefaultChar = NULL;
5254     getText.lpUsedDefChar = NULL;
5255     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5256     ok(strcmp(buffer, "\rX\r\r\r") == 0,
5257       "EM_GETTEXTEX returned incorrect string\n");
5258 
5259     /* Test number of lines reported after EM_REPLACESEL */
5260     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5261     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5262 
5263     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5264     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5265     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5266     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5267     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5268     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5269     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5270 
5271     /* Test the actual string */
5272     getText.cb = 1024;
5273     getText.codepage = CP_ACP;
5274     getText.flags = GT_DEFAULT;
5275     getText.lpDefaultChar = NULL;
5276     getText.lpUsedDefChar = NULL;
5277     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5278     ok(strcmp(buffer, "\r\r") == 0,
5279       "EM_GETTEXTEX returned incorrect string\n");
5280 
5281     /* Test number of lines reported after EM_REPLACESEL */
5282     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5283     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5284 
5285     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5286     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5287     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5288     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5289     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5290     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5291     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5292 
5293     /* Test the actual string */
5294     getText.cb = 1024;
5295     getText.codepage = CP_ACP;
5296     getText.flags = GT_DEFAULT;
5297     getText.lpDefaultChar = NULL;
5298     getText.lpUsedDefChar = NULL;
5299     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5300     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5301       "EM_GETTEXTEX returned incorrect string\n");
5302 
5303     /* Test number of lines reported after EM_REPLACESEL */
5304     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5305     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5306 
5307     /* Test with  multibyte character */
5308     if (!is_lang_japanese)
5309         skip("Skip multibyte character tests on non-Japanese platform\n");
5310     else
5311     {
5312         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5313         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5314         todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5315         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5316         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5317         ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5318         ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5319         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5320         ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5321         ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5322 
5323         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5324         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5325         todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5326         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5327         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5328         todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5329         todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5330         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5331         todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5332         todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5333     }
5334 
5335     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5336     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5337     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5338     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5339     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5340     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5341     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5342     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5343     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5344 
5345     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5346     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5347     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5348     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5349     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5350     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5351     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5352     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5353     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5354 
5355     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5356     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5357     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5358     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5359     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5360     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5361     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5362     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5363     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5364     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5365 
5366     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5367     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5368     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5369     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5370     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5371     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5372     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5373     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5374     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5375     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5376 
5377     if (!redraw)
5378         /* This is needed to avoid interfering with keybd_event calls
5379          * on other tests that simulate keyboard events. */
5380         SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5381 
5382     DestroyWindow(hwndRichEdit);
5383 
5384     /* Single-line richedit */
5385     hwndRichEdit = new_richedit_with_style(NULL, 0);
5386     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"line1\r\nline2");
5387     ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5388     r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5389     ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5390     ok(!strcmp(buffer, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buffer);
5391     DestroyWindow(hwndRichEdit);
5392 }
5393 
5394 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5395  * to test the state of the modifiers (Ctrl/Alt/Shift).
5396  *
5397  * Therefore Ctrl-<key> keystrokes need to be simulated with
5398  * keybd_event or by using SetKeyboardState to set the modifiers
5399  * and SendMessage to simulate the keystrokes.
5400  */
5401 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5402 {
5403     LRESULT result;
5404     hold_key(VK_CONTROL);
5405     result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5406     release_key(VK_CONTROL);
5407     return result;
5408 }
5409 
5410 static void test_WM_PASTE(void)
5411 {
5412     int result;
5413     char buffer[1024] = {0};
5414     const char* text1 = "testing paste\r";
5415     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5416     const char* text1_after = "testing paste\r\n";
5417     const char* text2 = "testing paste\r\rtesting paste";
5418     const char* text2_after = "testing paste\r\n\r\ntesting paste";
5419     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5420     HWND hwndRichEdit = new_richedit(NULL);
5421 
5422     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5423     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5424 
5425     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5426     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5427     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5428     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5429     /* Pasted text should be visible at this step */
5430     result = strcmp(text1_step1, buffer);
5431     ok(result == 0,
5432         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5433 
5434     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5435     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5436     /* Text should be the same as before (except for \r -> \r\n conversion) */
5437     result = strcmp(text1_after, buffer);
5438     ok(result == 0,
5439         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5440 
5441     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5442     SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5443     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5444     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5445     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5446     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5447     /* Pasted text should be visible at this step */
5448     result = strcmp(text3, buffer);
5449     ok(result == 0,
5450         "test paste: strcmp = %i\n", result);
5451     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5452     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5453     /* Text should be the same as before (except for \r -> \r\n conversion) */
5454     result = strcmp(text2_after, buffer);
5455     ok(result == 0,
5456         "test paste: strcmp = %i\n", result);
5457     send_ctrl_key(hwndRichEdit, 'Y');   /* Redo */
5458     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5459     /* Text should revert to post-paste state */
5460     result = strcmp(buffer,text3);
5461     ok(result == 0,
5462         "test paste: strcmp = %i\n", result);
5463 
5464     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5465     /* Send WM_CHAR to simulate Ctrl-V */
5466     SendMessageA(hwndRichEdit, WM_CHAR, 22,
5467                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5468     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5469     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5470     result = strcmp(buffer,"");
5471     ok(result == 0,
5472         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5473 
5474     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5475      * with SetKeyboard state. */
5476 
5477     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5478     /* Simulates paste (Ctrl-V) */
5479     hold_key(VK_CONTROL);
5480     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5481                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5482     release_key(VK_CONTROL);
5483     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5484     result = strcmp(buffer,"paste");
5485     ok(result == 0,
5486         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5487 
5488     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5489     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5490     /* Simulates copy (Ctrl-C) */
5491     hold_key(VK_CONTROL);
5492     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5493                 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5494     release_key(VK_CONTROL);
5495     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5496     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5497     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5498     result = strcmp(buffer,"testing");
5499     ok(result == 0,
5500         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5501 
5502     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5503     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5504     /* Simulates select all (Ctrl-A) */
5505     hold_key(VK_CONTROL);
5506     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5507                 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5508     /* Simulates select cut (Ctrl-X) */
5509     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5510                 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5511     release_key(VK_CONTROL);
5512     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5513     result = strcmp(buffer,"");
5514     ok(result == 0,
5515         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5516     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5517     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5518     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5519     result = strcmp(buffer,"cut\r\n");
5520     ok(result == 0,
5521         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5522     /* Simulates undo (Ctrl-Z) */
5523     hold_key(VK_CONTROL);
5524     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5525                 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5526     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5527     result = strcmp(buffer,"");
5528     ok(result == 0,
5529         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5530     /* Simulates redo (Ctrl-Y) */
5531     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5532                 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5533     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5534     result = strcmp(buffer,"cut\r\n");
5535     ok(result == 0,
5536         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5537     release_key(VK_CONTROL);
5538 
5539     /* Copy multiline text to clipboard for future use */
5540     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
5541     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5542     SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5543     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
5544 
5545     /* Paste into read-only control */
5546     result = SendMessageA(hwndRichEdit, EM_SETREADONLY, TRUE, 0);
5547     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5548     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5549     result = strcmp(buffer, text3);
5550     ok(result == 0,
5551         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5552 
5553     /* Cut from read-only control */
5554     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5555     SendMessageA(hwndRichEdit, WM_CUT, 0, 0);
5556     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5557     result = strcmp(buffer, text3);
5558     ok(result == 0,
5559         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5560 
5561     /* FIXME: Wine doesn't flush Ole clipboard when window is destroyed so do it manually */
5562     OleFlushClipboard();
5563     DestroyWindow(hwndRichEdit);
5564 
5565     /* Paste multi-line text into single-line control */
5566     hwndRichEdit = new_richedit_with_style(NULL, 0);
5567     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5568     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5569     result = strcmp(buffer, "testing paste");
5570     ok(result == 0,
5571         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5572     DestroyWindow(hwndRichEdit);
5573 }
5574 
5575 static void test_EM_FORMATRANGE(void)
5576 {
5577   int r, i, tpp_x, tpp_y;
5578   HDC hdc;
5579   HWND hwndRichEdit = new_richedit(NULL);
5580   FORMATRANGE fr;
5581   BOOL skip_non_english;
5582   static const struct {
5583     const char *string; /* The string */
5584     int first;          /* First 'pagebreak', 0 for don't care */
5585     int second;         /* Second 'pagebreak', 0 for don't care */
5586   } fmtstrings[] = {
5587     {"WINE wine", 0, 0},
5588     {"WINE wineWine", 0, 0},
5589     {"WINE\r\nwine\r\nwine", 5, 10},
5590     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5591     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5592   };
5593 
5594   skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5595   if (skip_non_english)
5596     skip("Skipping some tests on non-English platform\n");
5597 
5598   hdc = GetDC(hwndRichEdit);
5599   ok(hdc != NULL, "Could not get HDC\n");
5600 
5601   /* Calculate the twips per pixel */
5602   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5603   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5604 
5605   /* Test the simple case where all the text fits in the page rect. */
5606   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5607   fr.hdc = fr.hdcTarget = hdc;
5608   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5609   fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5610   fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5611   fr.chrg.cpMin = 0;
5612   fr.chrg.cpMax = -1;
5613   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5614   todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5615 
5616   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5617   fr.rc.bottom = fr.rcPage.bottom;
5618   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5619   todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5620 
5621   SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5622 
5623   for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5624   {
5625     GETTEXTLENGTHEX gtl;
5626     SIZE stringsize;
5627     int len;
5628 
5629     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5630 
5631     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5632     gtl.codepage = CP_ACP;
5633     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5634 
5635     /* Get some size information for the string */
5636     GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5637 
5638     /* Define the box to be half the width needed and a bit larger than the height.
5639      * Changes to the width means we have at least 2 pages. Changes to the height
5640      * is done so we can check the changing of fr.rc.bottom.
5641      */
5642     fr.hdc = fr.hdcTarget = hdc;
5643     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5644     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5645     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5646 
5647     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5648     todo_wine {
5649     ok(r == len, "Expected %d, got %d\n", len, r);
5650     }
5651 
5652     /* We know that the page can't hold the full string. See how many characters
5653      * are on the first one
5654      */
5655     fr.chrg.cpMin = 0;
5656     fr.chrg.cpMax = -1;
5657     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5658     todo_wine {
5659     if (! skip_non_english)
5660       ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5661     }
5662     if (fmtstrings[i].first)
5663       todo_wine {
5664       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5665       }
5666     else
5667       ok(r < len, "Expected < %d, got %d\n", len, r);
5668 
5669     /* Do another page */
5670     fr.chrg.cpMin = r;
5671     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5672     if (fmtstrings[i].second)
5673       todo_wine {
5674       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5675       }
5676     else if (! skip_non_english)
5677       ok (r < len, "Expected < %d, got %d\n", len, r);
5678 
5679     /* There is at least on more page, but we don't care */
5680 
5681     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5682     todo_wine {
5683     ok(r == len, "Expected %d, got %d\n", len, r);
5684     }
5685   }
5686 
5687   ReleaseDC(NULL, hdc);
5688   DestroyWindow(hwndRichEdit);
5689 }
5690 
5691 static int nCallbackCount = 0;
5692 
5693 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5694 				 LONG cb, LONG* pcb)
5695 {
5696   const char text[] = {'t','e','s','t'};
5697 
5698   if (sizeof(text) <= cb)
5699   {
5700     if ((int)dwCookie != nCallbackCount)
5701     {
5702       *pcb = 0;
5703       return 0;
5704     }
5705 
5706     memcpy (pbBuff, text, sizeof(text));
5707     *pcb = sizeof(text);
5708 
5709     nCallbackCount++;
5710 
5711     return 0;
5712   }
5713   else
5714     return 1; /* indicates callback failed */
5715 }
5716 
5717 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5718                                          LPBYTE pbBuff,
5719                                          LONG cb,
5720                                          LONG *pcb)
5721 {
5722   const char** str = (const char**)dwCookie;
5723   int size = strlen(*str);
5724   *pcb = cb;
5725   if (*pcb > size) {
5726     *pcb = size;
5727   }
5728   if (*pcb > 0) {
5729     memcpy(pbBuff, *str, *pcb);
5730     *str += *pcb;
5731   }
5732   return 0;
5733 }
5734 
5735 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5736                                          LPBYTE pbBuff,
5737                                          LONG cb,
5738                                          LONG *pcb)
5739 {
5740     DWORD *phase = (DWORD *)dwCookie;
5741 
5742     if(*phase == 0){
5743         static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5744         *pcb = sizeof(first) - 1;
5745         memcpy(pbBuff, first, *pcb);
5746     }else if(*phase == 1){
5747         static const char second[] = "\x8f\xc3\x8b";
5748         *pcb = sizeof(second) - 1;
5749         memcpy(pbBuff, second, *pcb);
5750     }else
5751         *pcb = 0;
5752 
5753     ++*phase;
5754 
5755     return 0;
5756 }
5757 
5758 struct StringWithLength {
5759     int length;
5760     char *buffer;
5761 };
5762 
5763 /* This callback is used to handled the null characters in a string. */
5764 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5765                                                    LPBYTE pbBuff,
5766                                                    LONG cb,
5767                                                    LONG *pcb)
5768 {
5769     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5770     int size = str->length;
5771     *pcb = cb;
5772     if (*pcb > size) {
5773       *pcb = size;
5774     }
5775     if (*pcb > 0) {
5776       memcpy(pbBuff, str->buffer, *pcb);
5777       str->buffer += *pcb;
5778       str->length -= *pcb;
5779     }
5780     return 0;
5781 }
5782 
5783 static void test_EM_STREAMIN(void)
5784 {
5785   HWND hwndRichEdit = new_richedit(NULL);
5786   DWORD phase;
5787   LRESULT result;
5788   EDITSTREAM es;
5789   char buffer[1024] = {0}, tmp[16];
5790   CHARRANGE range;
5791   PARAFORMAT2 fmt;
5792 
5793   const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5794   const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5795   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5796   const char * ptr;
5797 
5798   const char * streamText1 =
5799   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5800   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5801   "}\r\n";
5802 
5803   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5804   const char * streamText2 =
5805     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5806     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5807     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5808     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5809     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5810     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5811     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5812 
5813   const char * streamText3 = "RichEdit1";
5814 
5815   const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5816 
5817   const char * streamText4 =
5818       "This text just needs to be long enough to cause run to be split onto "
5819       "two separate lines and make sure the null terminating character is "
5820       "handled properly.\0";
5821 
5822   const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5823 
5824   int length4 = strlen(streamText4) + 1;
5825   struct StringWithLength cookieForStream4 = {
5826       length4,
5827       (char *)streamText4,
5828   };
5829 
5830   const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5831   int length5 = sizeof(streamText5) / sizeof(WCHAR);
5832   struct StringWithLength cookieForStream5 = {
5833       sizeof(streamText5),
5834       (char *)streamText5,
5835   };
5836 
5837   /* Minimal test without \par at the end */
5838   es.dwCookie = (DWORD_PTR)&streamText0;
5839   es.dwError = 0;
5840   es.pfnCallback = test_EM_STREAMIN_esCallback;
5841   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5842   ok(result == 12, "got %ld, expected %d\n", result, 12);
5843 
5844   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5845   ok (result  == 12,
5846       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5847   result = strcmp (buffer,"TestSomeText");
5848   ok (result  == 0,
5849       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5850   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5851   /* Show that para fmts are ignored */
5852   range.cpMin = 2;
5853   range.cpMax = 2;
5854   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5855   memset(&fmt, 0xcc, sizeof(fmt));
5856   fmt.cbSize = sizeof(fmt);
5857   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5858   ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5859   ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5860   ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5861   ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5862 
5863   /* Native richedit 2.0 ignores last \par */
5864   ptr = streamText0a;
5865   es.dwCookie = (DWORD_PTR)&ptr;
5866   es.dwError = 0;
5867   es.pfnCallback = test_EM_STREAMIN_esCallback;
5868   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5869   ok(result == 12, "got %ld, expected %d\n", result, 12);
5870 
5871   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5872   ok (result  == 12,
5873       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5874   result = strcmp (buffer,"TestSomeText");
5875   ok (result  == 0,
5876       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5877   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5878   /* This time para fmts are processed */
5879   range.cpMin = 2;
5880   range.cpMax = 2;
5881   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5882   memset(&fmt, 0xcc, sizeof(fmt));
5883   fmt.cbSize = sizeof(fmt);
5884   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5885   ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5886   ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5887   ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5888   ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5889 
5890   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5891   es.dwCookie = (DWORD_PTR)&streamText0b;
5892   es.dwError = 0;
5893   es.pfnCallback = test_EM_STREAMIN_esCallback;
5894   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5895   ok(result == 13, "got %ld, expected %d\n", result, 13);
5896 
5897   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5898   ok (result  == 14,
5899       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5900   result = strcmp (buffer,"TestSomeText\r\n");
5901   ok (result  == 0,
5902       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5903   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5904 
5905   /* Show that when using SFF_SELECTION the last \par is not ignored. */
5906   ptr = streamText0a;
5907   es.dwCookie = (DWORD_PTR)&ptr;
5908   es.dwError = 0;
5909   es.pfnCallback = test_EM_STREAMIN_esCallback;
5910   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5911   ok(result == 12, "got %ld, expected %d\n", result, 12);
5912 
5913   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5914   ok (result  == 12,
5915       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5916   result = strcmp (buffer,"TestSomeText");
5917   ok (result  == 0,
5918       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5919   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5920 
5921   range.cpMin = 0;
5922   range.cpMax = -1;
5923   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5924   ok (result == 13, "got %ld\n", result);
5925 
5926   ptr = streamText0a;
5927   es.dwCookie = (DWORD_PTR)&ptr;
5928   es.dwError = 0;
5929   es.pfnCallback = test_EM_STREAMIN_esCallback;
5930 
5931   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5932   ok(result == 13, "got %ld, expected 13\n", result);
5933 
5934   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5935   ok (result  == 14,
5936       "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5937   result = strcmp (buffer,"TestSomeText\r\n");
5938   ok (result  == 0,
5939       "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5940   ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5941 
5942   es.dwCookie = (DWORD_PTR)&streamText1;
5943   es.dwError = 0;
5944   es.pfnCallback = test_EM_STREAMIN_esCallback;
5945   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5946   ok(result == 12, "got %ld, expected %d\n", result, 12);
5947 
5948   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5949   ok (result  == 12,
5950       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5951   result = strcmp (buffer,"TestSomeText");
5952   ok (result  == 0,
5953       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5954   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5955 
5956   es.dwCookie = (DWORD_PTR)&streamText2;
5957   es.dwError = 0;
5958   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5959   ok(result == 0, "got %ld, expected %d\n", result, 0);
5960 
5961   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5962   ok (result  == 0,
5963       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5964   ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5965   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5966 
5967   es.dwCookie = (DWORD_PTR)&streamText3;
5968   es.dwError = 0;
5969   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5970   ok(result == 0, "got %ld, expected %d\n", result, 0);
5971 
5972   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5973   ok (result  == 0,
5974       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5975   ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5976   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5977 
5978   es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5979   es.dwError = 0;
5980   es.pfnCallback = test_EM_STREAMIN_esCallback;
5981   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5982   ok(result == 18, "got %ld, expected %d\n", result, 18);
5983 
5984   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5985   ok(result  == 15,
5986       "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5987   result = strcmp (buffer,"TestUTF8WithBOM");
5988   ok(result  == 0,
5989       "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5990   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5991 
5992   phase = 0;
5993   es.dwCookie = (DWORD_PTR)&phase;
5994   es.dwError = 0;
5995   es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5996   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5997   ok(result == 8, "got %ld\n", result);
5998 
5999   WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
6000 
6001   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6002   ok(result  == 3,
6003       "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
6004   result = memcmp (buffer, tmp, 3);
6005   ok(result  == 0,
6006       "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
6007   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
6008 
6009   es.dwCookie = (DWORD_PTR)&cookieForStream4;
6010   es.dwError = 0;
6011   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6012   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6013   ok(result == length4, "got %ld, expected %d\n", result, length4);
6014 
6015   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6016   ok (result  == length4,
6017       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
6018   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
6019 
6020   es.dwCookie = (DWORD_PTR)&cookieForStream5;
6021   es.dwError = 0;
6022   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6023   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
6024   ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
6025 
6026   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6027   ok (result  == length5,
6028       "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
6029   ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
6030 
6031   DestroyWindow(hwndRichEdit);
6032 
6033   /* Single-line richedit */
6034   hwndRichEdit = new_richedit_with_style(NULL, 0);
6035   ptr = "line1\r\nline2";
6036   es.dwCookie = (DWORD_PTR)&ptr;
6037   es.dwError = 0;
6038   es.pfnCallback = test_EM_STREAMIN_esCallback;
6039   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6040   ok(result == 12, "got %ld, expected %d\n", result, 12);
6041   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6042   ok (!strcmp(buffer, "line1"),
6043       "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6044 }
6045 
6046 static void test_EM_StreamIn_Undo(void)
6047 {
6048   /* The purpose of this test is to determine when a EM_StreamIn should be
6049    * undoable. This is important because WM_PASTE currently uses StreamIn and
6050    * pasting should always be undoable but streaming isn't always.
6051    *
6052    * cases to test:
6053    * StreamIn plain text without SFF_SELECTION.
6054    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
6055    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
6056    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
6057    * Feel free to add tests for other text modes or StreamIn things.
6058    */
6059 
6060 
6061   HWND hwndRichEdit = new_richedit(NULL);
6062   LRESULT result;
6063   EDITSTREAM es;
6064   char buffer[1024] = {0};
6065   const char randomtext[] = "Some text";
6066 
6067   es.pfnCallback = EditStreamCallback;
6068 
6069   /* StreamIn, no SFF_SELECTION */
6070   es.dwCookie = nCallbackCount;
6071   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6072   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6073   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6074   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6075   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6076   result = strcmp (buffer,"test");
6077   ok (result  == 0,
6078       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6079 
6080   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6081   ok (result == FALSE,
6082       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
6083 
6084   /* StreamIn, SFF_SELECTION, but nothing selected */
6085   es.dwCookie = nCallbackCount;
6086   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6087   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6088   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6089   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6090   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6091   result = strcmp (buffer,"testSome text");
6092   ok (result  == 0,
6093       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6094 
6095   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6096   ok (result == TRUE,
6097      "EM_STREAMIN with SFF_SELECTION but no selection set "
6098       "should create an undo\n");
6099 
6100   /* StreamIn, SFF_SELECTION, with a selection */
6101   es.dwCookie = nCallbackCount;
6102   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6103   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6104   SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6105   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6106   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6107   result = strcmp (buffer,"Sometesttext");
6108   ok (result  == 0,
6109       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6110 
6111   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6112   ok (result == TRUE,
6113       "EM_STREAMIN with SFF_SELECTION and selection set "
6114       "should create an undo\n");
6115 
6116   DestroyWindow(hwndRichEdit);
6117 }
6118 
6119 static BOOL is_em_settextex_supported(HWND hwnd)
6120 {
6121     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6122     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6123 }
6124 
6125 static void test_unicode_conversions(void)
6126 {
6127     static const WCHAR tW[] = {'t',0};
6128     static const WCHAR teW[] = {'t','e',0};
6129     static const WCHAR textW[] = {'t','e','s','t',0};
6130     static const char textA[] = "test";
6131     char bufA[64];
6132     WCHAR bufW[64];
6133     HWND hwnd;
6134     int em_settextex_supported, ret;
6135 
6136 #define set_textA(hwnd, wm_set_text, txt) \
6137     do { \
6138         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6139         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6140         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6141         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6142         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
6143     } while(0)
6144 #define expect_textA(hwnd, wm_get_text, txt) \
6145     do { \
6146         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6147         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6148         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6149         memset(bufA, 0xAA, sizeof(bufA)); \
6150         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6151         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
6152         ret = lstrcmpA(bufA, txt); \
6153         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6154     } while(0)
6155 
6156 #define set_textW(hwnd, wm_set_text, txt) \
6157     do { \
6158         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6159         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6160         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6161         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6162         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
6163     } while(0)
6164 #define expect_textW(hwnd, wm_get_text, txt) \
6165     do { \
6166         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6167         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6168         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6169         memset(bufW, 0xAA, sizeof(bufW)); \
6170         ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6171         ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
6172         ret = lstrcmpW(bufW, txt); \
6173         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6174     } while(0)
6175 #define expect_empty(hwnd, wm_get_text) \
6176     do { \
6177         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6178         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6179         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6180         memset(bufA, 0xAA, sizeof(bufA)); \
6181         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6182         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6183         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6184     } while(0)
6185 
6186     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6187                            0, 0, 200, 60, 0, 0, 0, 0);
6188     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6189 
6190     ret = IsWindowUnicode(hwnd);
6191     ok(ret, "RichEdit20W should be unicode under NT\n");
6192 
6193     /* EM_SETTEXTEX is supported starting from version 3.0 */
6194     em_settextex_supported = is_em_settextex_supported(hwnd);
6195     trace("EM_SETTEXTEX is %ssupported on this platform\n",
6196           em_settextex_supported ? "" : "NOT ");
6197 
6198     expect_empty(hwnd, WM_GETTEXT);
6199     expect_empty(hwnd, EM_GETTEXTEX);
6200 
6201     ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6202     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6203     expect_textA(hwnd, WM_GETTEXT, "t");
6204     expect_textA(hwnd, EM_GETTEXTEX, "t");
6205     expect_textW(hwnd, EM_GETTEXTEX, tW);
6206 
6207     ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6208     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6209     expect_textA(hwnd, WM_GETTEXT, "te");
6210     expect_textA(hwnd, EM_GETTEXTEX, "te");
6211     expect_textW(hwnd, EM_GETTEXTEX, teW);
6212 
6213     set_textA(hwnd, WM_SETTEXT, NULL);
6214     expect_empty(hwnd, WM_GETTEXT);
6215     expect_empty(hwnd, EM_GETTEXTEX);
6216 
6217     set_textA(hwnd, WM_SETTEXT, textA);
6218     expect_textA(hwnd, WM_GETTEXT, textA);
6219     expect_textA(hwnd, EM_GETTEXTEX, textA);
6220     expect_textW(hwnd, EM_GETTEXTEX, textW);
6221 
6222     if (em_settextex_supported)
6223     {
6224         set_textA(hwnd, EM_SETTEXTEX, textA);
6225         expect_textA(hwnd, WM_GETTEXT, textA);
6226         expect_textA(hwnd, EM_GETTEXTEX, textA);
6227         expect_textW(hwnd, EM_GETTEXTEX, textW);
6228     }
6229 
6230     set_textW(hwnd, WM_SETTEXT, textW);
6231     expect_textW(hwnd, WM_GETTEXT, textW);
6232     expect_textA(hwnd, WM_GETTEXT, textA);
6233     expect_textW(hwnd, EM_GETTEXTEX, textW);
6234     expect_textA(hwnd, EM_GETTEXTEX, textA);
6235 
6236     if (em_settextex_supported)
6237     {
6238         set_textW(hwnd, EM_SETTEXTEX, textW);
6239         expect_textW(hwnd, WM_GETTEXT, textW);
6240         expect_textA(hwnd, WM_GETTEXT, textA);
6241         expect_textW(hwnd, EM_GETTEXTEX, textW);
6242         expect_textA(hwnd, EM_GETTEXTEX, textA);
6243     }
6244     DestroyWindow(hwnd);
6245 
6246     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6247                            0, 0, 200, 60, 0, 0, 0, 0);
6248     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6249 
6250     ret = IsWindowUnicode(hwnd);
6251     ok(!ret, "RichEdit20A should NOT be unicode\n");
6252 
6253     set_textA(hwnd, WM_SETTEXT, textA);
6254     expect_textA(hwnd, WM_GETTEXT, textA);
6255     expect_textA(hwnd, EM_GETTEXTEX, textA);
6256     expect_textW(hwnd, EM_GETTEXTEX, textW);
6257 
6258     if (em_settextex_supported)
6259     {
6260         set_textA(hwnd, EM_SETTEXTEX, textA);
6261         expect_textA(hwnd, WM_GETTEXT, textA);
6262         expect_textA(hwnd, EM_GETTEXTEX, textA);
6263         expect_textW(hwnd, EM_GETTEXTEX, textW);
6264     }
6265 
6266         set_textW(hwnd, WM_SETTEXT, textW);
6267         expect_textW(hwnd, WM_GETTEXT, textW);
6268         expect_textA(hwnd, WM_GETTEXT, textA);
6269         expect_textW(hwnd, EM_GETTEXTEX, textW);
6270         expect_textA(hwnd, EM_GETTEXTEX, textA);
6271 
6272     if (em_settextex_supported)
6273     {
6274         set_textW(hwnd, EM_SETTEXTEX, textW);
6275         expect_textW(hwnd, WM_GETTEXT, textW);
6276         expect_textA(hwnd, WM_GETTEXT, textA);
6277         expect_textW(hwnd, EM_GETTEXTEX, textW);
6278         expect_textA(hwnd, EM_GETTEXTEX, textA);
6279     }
6280     DestroyWindow(hwnd);
6281 }
6282 
6283 static void test_WM_CHAR(void)
6284 {
6285     HWND hwnd;
6286     int ret;
6287     const char * char_list = "abc\rabc\r";
6288     const char * expected_content_single = "abcabc";
6289     const char * expected_content_multi = "abc\r\nabc\r\n";
6290     char buffer[64] = {0};
6291     const char * p;
6292 
6293     /* single-line control must IGNORE carriage returns */
6294     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6295                            0, 0, 200, 60, 0, 0, 0, 0);
6296     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6297 
6298     p = char_list;
6299     while (*p != '\0') {
6300         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6301         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6302         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6303         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6304         p++;
6305     }
6306 
6307     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6308     ret = strcmp(buffer, expected_content_single);
6309     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6310 
6311     DestroyWindow(hwnd);
6312 
6313     /* multi-line control inserts CR normally */
6314     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6315                            0, 0, 200, 60, 0, 0, 0, 0);
6316     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6317 
6318     p = char_list;
6319     while (*p != '\0') {
6320         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6321         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6322         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6323         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6324         p++;
6325     }
6326 
6327     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6328     ret = strcmp(buffer, expected_content_multi);
6329     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6330 
6331     DestroyWindow(hwnd);
6332 }
6333 
6334 static void test_EM_GETTEXTLENGTHEX(void)
6335 {
6336     HWND hwnd;
6337     GETTEXTLENGTHEX gtl;
6338     int ret;
6339     const char * base_string = "base string";
6340     const char * test_string = "a\nb\n\n\r\n";
6341     const char * test_string_after = "a";
6342     const char * test_string_2 = "a\rtest\rstring";
6343     char buffer[64] = {0};
6344 
6345     /* single line */
6346     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6347                            0, 0, 200, 60, 0, 0, 0, 0);
6348     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6349 
6350     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6351     gtl.codepage = CP_ACP;
6352     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6353     ok(ret == 0, "ret %d\n",ret);
6354 
6355     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6356     gtl.codepage = CP_ACP;
6357     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6358     ok(ret == 0, "ret %d\n",ret);
6359 
6360     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6361 
6362     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6363     gtl.codepage = CP_ACP;
6364     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6365     ok(ret == strlen(base_string), "ret %d\n",ret);
6366 
6367     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6368     gtl.codepage = CP_ACP;
6369     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6370     ok(ret == strlen(base_string), "ret %d\n",ret);
6371 
6372     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6373 
6374     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6375     gtl.codepage = CP_ACP;
6376     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6377     ok(ret == 1, "ret %d\n",ret);
6378 
6379     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6380     gtl.codepage = CP_ACP;
6381     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6382     ok(ret == 1, "ret %d\n",ret);
6383 
6384     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6385     ret = strcmp(buffer, test_string_after);
6386     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6387 
6388     DestroyWindow(hwnd);
6389 
6390     /* multi line */
6391     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6392                            0, 0, 200, 60, 0, 0, 0, 0);
6393     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6394 
6395     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6396     gtl.codepage = CP_ACP;
6397     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6398     ok(ret == 0, "ret %d\n",ret);
6399 
6400     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6401     gtl.codepage = CP_ACP;
6402     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6403     ok(ret == 0, "ret %d\n",ret);
6404 
6405     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6406 
6407     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6408     gtl.codepage = CP_ACP;
6409     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6410     ok(ret == strlen(base_string), "ret %d\n",ret);
6411 
6412     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6413     gtl.codepage = CP_ACP;
6414     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6415     ok(ret == strlen(base_string), "ret %d\n",ret);
6416 
6417     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6418 
6419     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6420     gtl.codepage = CP_ACP;
6421     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6422     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6423 
6424     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6425     gtl.codepage = CP_ACP;
6426     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6427     ok(ret == strlen(test_string_2), "ret %d\n",ret);
6428 
6429     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6430 
6431     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6432     gtl.codepage = CP_ACP;
6433     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6434     ok(ret == 10, "ret %d\n",ret);
6435 
6436     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6437     gtl.codepage = CP_ACP;
6438     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6439     ok(ret == 6, "ret %d\n",ret);
6440 
6441     /* Unicode/NUMCHARS/NUMBYTES */
6442     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6443 
6444     gtl.flags = GTL_DEFAULT;
6445     gtl.codepage = 1200;
6446     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6447     ok(ret == lstrlenA(test_string_2),
6448        "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6449 
6450     gtl.flags = GTL_NUMCHARS;
6451     gtl.codepage = 1200;
6452     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6453     ok(ret == lstrlenA(test_string_2),
6454        "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6455 
6456     gtl.flags = GTL_NUMBYTES;
6457     gtl.codepage = 1200;
6458     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6459     ok(ret == lstrlenA(test_string_2)*2,
6460        "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6461 
6462     gtl.flags = GTL_PRECISE;
6463     gtl.codepage = 1200;
6464     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6465     ok(ret == lstrlenA(test_string_2)*2,
6466        "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6467 
6468     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6469     gtl.codepage = 1200;
6470     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6471     ok(ret == lstrlenA(test_string_2),
6472        "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6473 
6474     gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6475     gtl.codepage = 1200;
6476     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6477     ok(ret == E_INVALIDARG,
6478        "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6479 
6480     DestroyWindow(hwnd);
6481 }
6482 
6483 
6484 /* globals that parent and child access when checking event masks & notifications */
6485 static HWND eventMaskEditHwnd = 0;
6486 static int queriedEventMask;
6487 static int watchForEventMask = 0;
6488 
6489 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6490 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6491 {
6492     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6493     {
6494       queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6495     }
6496     return DefWindowProcA(hwnd, message, wParam, lParam);
6497 }
6498 
6499 /* test event masks in combination with WM_COMMAND */
6500 static void test_eventMask(void)
6501 {
6502     HWND parent;
6503     int ret, style;
6504     WNDCLASSA cls;
6505     const char text[] = "foo bar\n";
6506     int eventMask;
6507 
6508     /* register class to capture WM_COMMAND */
6509     cls.style = 0;
6510     cls.lpfnWndProc = ParentMsgCheckProcA;
6511     cls.cbClsExtra = 0;
6512     cls.cbWndExtra = 0;
6513     cls.hInstance = GetModuleHandleA(0);
6514     cls.hIcon = 0;
6515     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6516     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6517     cls.lpszMenuName = NULL;
6518     cls.lpszClassName = "EventMaskParentClass";
6519     if(!RegisterClassA(&cls)) assert(0);
6520 
6521     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6522                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6523     ok (parent != 0, "Failed to create parent window\n");
6524 
6525     eventMaskEditHwnd = new_richedit(parent);
6526     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6527 
6528     eventMask = ENM_CHANGE | ENM_UPDATE;
6529     ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6530     ok(ret == ENM_NONE, "wrong event mask\n");
6531     ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6532     ok(ret == eventMask, "failed to set event mask\n");
6533 
6534     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6535     queriedEventMask = 0;  /* initialize to something other than we expect */
6536     watchForEventMask = EN_CHANGE;
6537     ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6538     ok(ret == TRUE, "failed to set text\n");
6539     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6540        notification in response to WM_SETTEXT */
6541     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6542             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6543 
6544     /* check to see if EN_CHANGE is sent when redraw is turned off */
6545     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6546     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6547     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6548     /* redraw is disabled by making the window invisible. */
6549     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6550     queriedEventMask = 0;  /* initialize to something other than we expect */
6551     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6552     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6553             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6554     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6555     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6556 
6557     /* check to see if EN_UPDATE is sent when the editor isn't visible */
6558     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6559     style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6560     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6561     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6562     watchForEventMask = EN_UPDATE;
6563     queriedEventMask = 0;  /* initialize to something other than we expect */
6564     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6565     ok(queriedEventMask == 0,
6566             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6567     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6568     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6569     queriedEventMask = 0;  /* initialize to something other than we expect */
6570     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6571     ok(queriedEventMask == eventMask,
6572             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6573 
6574 
6575     DestroyWindow(parent);
6576 }
6577 
6578 static int received_WM_NOTIFY = 0;
6579 static int modify_at_WM_NOTIFY = 0;
6580 static BOOL filter_on_WM_NOTIFY = FALSE;
6581 static HWND hwndRichedit_WM_NOTIFY;
6582 
6583 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6584 {
6585     if(message == WM_NOTIFY)
6586     {
6587       received_WM_NOTIFY = 1;
6588       modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6589       if (filter_on_WM_NOTIFY) return TRUE;
6590     }
6591     return DefWindowProcA(hwnd, message, wParam, lParam);
6592 }
6593 
6594 static void test_WM_NOTIFY(void)
6595 {
6596     HWND parent;
6597     WNDCLASSA cls;
6598     CHARFORMAT2A cf2;
6599     int sel_start, sel_end;
6600 
6601     /* register class to capture WM_NOTIFY */
6602     cls.style = 0;
6603     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6604     cls.cbClsExtra = 0;
6605     cls.cbWndExtra = 0;
6606     cls.hInstance = GetModuleHandleA(0);
6607     cls.hIcon = 0;
6608     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6609     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6610     cls.lpszMenuName = NULL;
6611     cls.lpszClassName = "WM_NOTIFY_ParentClass";
6612     if(!RegisterClassA(&cls)) assert(0);
6613 
6614     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6615                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6616     ok (parent != 0, "Failed to create parent window\n");
6617 
6618     hwndRichedit_WM_NOTIFY = new_richedit(parent);
6619     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6620 
6621     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6622 
6623     /* Notifications for selection change should only be sent when selection
6624        actually changes. EM_SETCHARFORMAT is one message that calls
6625        ME_CommitUndo, which should check whether message should be sent */
6626     received_WM_NOTIFY = 0;
6627     cf2.cbSize = sizeof(CHARFORMAT2A);
6628     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6629     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6630     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6631     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6632     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6633 
6634     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6635        already at 0. */
6636     received_WM_NOTIFY = 0;
6637     modify_at_WM_NOTIFY = 0;
6638     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6639     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6640     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6641 
6642     received_WM_NOTIFY = 0;
6643     modify_at_WM_NOTIFY = 0;
6644     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6645     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6646 
6647     received_WM_NOTIFY = 0;
6648     modify_at_WM_NOTIFY = 0;
6649     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6650     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6651     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6652 
6653     /* Test for WM_NOTIFY messages with redraw disabled. */
6654     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6655     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6656     received_WM_NOTIFY = 0;
6657     SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6658     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6659     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6660 
6661     /* Test filtering key events. */
6662     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6663     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6664     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6665     received_WM_NOTIFY = 0;
6666     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6667     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6668     ok(sel_start == 1 && sel_end == 1,
6669        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6670     filter_on_WM_NOTIFY = TRUE;
6671     received_WM_NOTIFY = 0;
6672     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6673     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6674     ok(sel_start == 1 && sel_end == 1,
6675        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6676 
6677     /* test with owner set to NULL */
6678     SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6679     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6680     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6681     ok(sel_start == 1 && sel_end == 1,
6682        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6683 
6684     DestroyWindow(hwndRichedit_WM_NOTIFY);
6685     DestroyWindow(parent);
6686 }
6687 
6688 static ENLINK enlink;
6689 #define CURSOR_CLIENT_X 5
6690 #define CURSOR_CLIENT_Y 5
6691 #define WP_PARENT 1
6692 #define WP_CHILD 2
6693 
6694 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6695 {
6696     if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6697     {
6698         enlink = *(ENLINK*)lParam;
6699     }
6700     return DefWindowProcA(hwnd, message, wParam, lParam);
6701 }
6702 
6703 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6704                              UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6705 {
6706     ENLINK junk_enlink;
6707 
6708     switch (msg)
6709     {
6710     case WM_LBUTTONDBLCLK:
6711     case WM_LBUTTONDOWN:
6712     case WM_LBUTTONUP:
6713     case WM_MOUSEHOVER:
6714     case WM_MOUSEMOVE:
6715     case WM_MOUSEWHEEL:
6716     case WM_RBUTTONDBLCLK:
6717     case WM_RBUTTONDOWN:
6718     case WM_RBUTTONUP:
6719         lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6720         break;
6721     case WM_SETCURSOR:
6722         if (wParam == WP_PARENT)
6723             wParam = (WPARAM)parent;
6724         else if (wParam == WP_CHILD)
6725             wParam = (WPARAM)hwnd;
6726         break;
6727     }
6728 
6729     memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6730     enlink = junk_enlink;
6731 
6732     SendMessageA(hwnd, msg, wParam, lParam);
6733 
6734     if (notifies)
6735     {
6736         ok(enlink.nmhdr.hwndFrom == hwnd,
6737            "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6738         ok(enlink.nmhdr.idFrom == 0,
6739            "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6740         ok(enlink.msg == msg,
6741            "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6742         if (msg == WM_SETCURSOR)
6743         {
6744             ok(enlink.wParam == 0,
6745                "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6746         }
6747         else
6748         {
6749             ok(enlink.wParam == wParam,
6750                "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6751         }
6752         ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6753            "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6754            desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6755         ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6756            "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6757     }
6758     else
6759     {
6760         ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6761            "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6762     }
6763 }
6764 
6765 static void test_EN_LINK(void)
6766 {
6767     HWND hwnd, parent;
6768     WNDCLASSA cls;
6769     CHARFORMAT2A cf2;
6770     POINT orig_cursor_pos;
6771     POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6772     int i;
6773 
6774     static const struct
6775     {
6776         UINT msg;
6777         WPARAM wParam;
6778         LPARAM lParam;
6779         BOOL notifies;
6780     }
6781     link_notify_tests[] =
6782     {
6783         /* hold down the left button and try some messages */
6784         { WM_LBUTTONDOWN,    0,          0,  TRUE  }, /* 0 */
6785         { EM_LINESCROLL,     0,          1,  FALSE },
6786         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6787         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6788         { WM_MOUSEHOVER,     0,          0,  FALSE },
6789         { WM_MOUSEMOVE,      0,          0,  FALSE },
6790         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6791         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6792         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6793         { WM_RBUTTONUP,      0,          0,  TRUE  },
6794         { WM_SETCURSOR,      0,          0,  FALSE },
6795         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6796         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6797         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6798         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6799         { WM_LBUTTONUP,      0,          0,  TRUE  },
6800         /* hold down the right button and try some messages */
6801         { WM_RBUTTONDOWN,    0,          0,  TRUE  }, /* 16 */
6802         { EM_LINESCROLL,     0,          1,  FALSE },
6803         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6804         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6805         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6806         { WM_LBUTTONUP,      0,          0,  TRUE  },
6807         { WM_MOUSEHOVER,     0,          0,  FALSE },
6808         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6809         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6810         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6811         { WM_SETCURSOR,      0,          0,  FALSE },
6812         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6813         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6814         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6815         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6816         { WM_RBUTTONUP,      0,          0,  TRUE  },
6817         /* try the messages with both buttons released */
6818         { EM_LINESCROLL,     0,          1,  FALSE }, /* 32 */
6819         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6820         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6821         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6822         { WM_LBUTTONUP,      0,          0,  TRUE  },
6823         { WM_MOUSEHOVER,     0,          0,  FALSE },
6824         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6825         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6826         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6827         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6828         { WM_RBUTTONUP,      0,          0,  TRUE  },
6829         { WM_SETCURSOR,      0,          0,  FALSE },
6830         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6831         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6832         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6833         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE }
6834     };
6835 
6836     /* register class to capture WM_NOTIFY */
6837     cls.style = 0;
6838     cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6839     cls.cbClsExtra = 0;
6840     cls.cbWndExtra = 0;
6841     cls.hInstance = GetModuleHandleA(0);
6842     cls.hIcon = 0;
6843     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6844     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6845     cls.lpszMenuName = NULL;
6846     cls.lpszClassName = "EN_LINK_ParentClass";
6847     if(!RegisterClassA(&cls)) assert(0);
6848 
6849     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6850                            0, 0, 200, 60, NULL, NULL, NULL, NULL);
6851     ok(parent != 0, "Failed to create parent window\n");
6852 
6853     hwnd = new_richedit(parent);
6854     ok(hwnd != 0, "Failed to create edit window\n");
6855 
6856     SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6857 
6858     cf2.cbSize = sizeof(CHARFORMAT2A);
6859     cf2.dwMask = CFM_LINK;
6860     cf2.dwEffects = CFE_LINK;
6861     SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6862     /* mixing letters and numbers causes runs to be split */
6863     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6864 
6865     GetCursorPos(&orig_cursor_pos);
6866     SetCursorPos(0, 0);
6867 
6868     for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6869     {
6870         link_notify_test("cursor position simulated", i, hwnd, parent,
6871                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6872                          link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6873     }
6874 
6875     ClientToScreen(hwnd, &cursor_screen_pos);
6876     SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6877 
6878     for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6879     {
6880         link_notify_test("cursor position set", i, hwnd, parent,
6881                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6882                          link_notify_tests[i].notifies);
6883     }
6884 
6885     SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6886     DestroyWindow(hwnd);
6887     DestroyWindow(parent);
6888 }
6889 
6890 static void test_undo_coalescing(void)
6891 {
6892     HWND hwnd;
6893     int result;
6894     char buffer[64] = {0};
6895 
6896     /* multi-line control inserts CR normally */
6897     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6898                            0, 0, 200, 60, 0, 0, 0, 0);
6899     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6900 
6901     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6902     ok (result == FALSE, "Can undo after window creation.\n");
6903     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6904     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6905     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6906     ok (result == FALSE, "Can redo after window creation.\n");
6907     result = SendMessageA(hwnd, EM_REDO, 0, 0);
6908     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6909 
6910     /* Test the effect of arrows keys during typing on undo transactions*/
6911     simulate_typing_characters(hwnd, "one two three");
6912     SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6913     SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6914     simulate_typing_characters(hwnd, " four five six");
6915 
6916     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6917     ok (result == FALSE, "Can redo before anything is undone.\n");
6918     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6919     ok (result == TRUE, "Cannot undo typed characters.\n");
6920     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6921     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6922     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6923     ok (result == TRUE, "Cannot redo after undo.\n");
6924     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6925     result = strcmp(buffer, "one two three");
6926     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6927 
6928     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6929     ok (result == TRUE, "Cannot undo typed characters.\n");
6930     result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6931     ok (result == TRUE, "Failed to undo typed characters.\n");
6932     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6933     result = strcmp(buffer, "");
6934     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6935 
6936     /* Test the effect of focus changes during typing on undo transactions*/
6937     simulate_typing_characters(hwnd, "one two three");
6938     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6939     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6940     SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6941     SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6942     simulate_typing_characters(hwnd, " four five six");
6943     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6944     ok (result == TRUE, "Failed to undo typed characters.\n");
6945     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6946     result = strcmp(buffer, "one two three");
6947     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6948 
6949     /* Test the effect of the back key during typing on undo transactions */
6950     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6951     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6952     ok (result == TRUE, "Failed to clear the text.\n");
6953     simulate_typing_characters(hwnd, "one two threa");
6954     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6955     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6956     SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6957     SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6958     simulate_typing_characters(hwnd, "e four five six");
6959     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6960     ok (result == TRUE, "Failed to undo typed characters.\n");
6961     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6962     result = strcmp(buffer, "");
6963     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6964 
6965     /* Test the effect of the delete key during typing on undo transactions */
6966     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6967     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6968     ok(result == TRUE, "Failed to set the text.\n");
6969     SendMessageA(hwnd, EM_SETSEL, 1, 1);
6970     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6971     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6972     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6973     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6974     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6975     ok (result == TRUE, "Failed to undo typed characters.\n");
6976     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6977     result = strcmp(buffer, "acd");
6978     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6979     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6980     ok (result == TRUE, "Failed to undo typed characters.\n");
6981     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6982     result = strcmp(buffer, "abcd");
6983     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6984 
6985     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6986     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6987     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6988     ok (result == TRUE, "Failed to clear the text.\n");
6989     simulate_typing_characters(hwnd, "one two three");
6990     result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6991     ok (result == 0, "expected %d but got %d\n", 0, result);
6992     simulate_typing_characters(hwnd, " four five six");
6993     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6994     ok (result == TRUE, "Failed to undo typed characters.\n");
6995     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6996     result = strcmp(buffer, "one two three");
6997     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6998     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6999     ok (result == TRUE, "Failed to undo typed characters.\n");
7000     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7001     result = strcmp(buffer, "");
7002     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7003 
7004     DestroyWindow(hwnd);
7005 }
7006 
7007 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
7008 {
7009     int length;
7010 
7011     /* MSDN lied, length is actually the number of bytes. */
7012     length = bytes / sizeof(WCHAR);
7013     switch(code)
7014     {
7015         case WB_ISDELIMITER:
7016             return text[pos] == 'X';
7017         case WB_LEFT:
7018         case WB_MOVEWORDLEFT:
7019             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7020                 return pos-1;
7021             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
7022         case WB_LEFTBREAK:
7023             pos--;
7024             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7025                 pos--;
7026             return pos;
7027         case WB_RIGHT:
7028         case WB_MOVEWORDRIGHT:
7029             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7030                 return pos+1;
7031             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
7032         case WB_RIGHTBREAK:
7033             pos++;
7034             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7035                 pos++;
7036             return pos;
7037         default:
7038             ok(FALSE, "Unexpected code %d\n", code);
7039             break;
7040     }
7041     return 0;
7042 }
7043 
7044 static void test_word_movement(void)
7045 {
7046     HWND hwnd;
7047     int result;
7048     int sel_start, sel_end;
7049     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
7050 
7051     /* multi-line control inserts CR normally */
7052     hwnd = new_richedit(NULL);
7053 
7054     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
7055     ok (result == TRUE, "Failed to clear the text.\n");
7056     SendMessageA(hwnd, EM_SETSEL, 0, 0);
7057     /* |one two three */
7058 
7059     send_ctrl_key(hwnd, VK_RIGHT);
7060     /* one |two  three */
7061     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7062     ok(sel_start == sel_end, "Selection should be empty\n");
7063     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7064 
7065     send_ctrl_key(hwnd, VK_RIGHT);
7066     /* one two  |three */
7067     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7068     ok(sel_start == sel_end, "Selection should be empty\n");
7069     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7070 
7071     send_ctrl_key(hwnd, VK_LEFT);
7072     /* one |two  three */
7073     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7074     ok(sel_start == sel_end, "Selection should be empty\n");
7075     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7076 
7077     send_ctrl_key(hwnd, VK_LEFT);
7078     /* |one two  three */
7079     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7080     ok(sel_start == sel_end, "Selection should be empty\n");
7081     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
7082 
7083     SendMessageA(hwnd, EM_SETSEL, 8, 8);
7084     /* one two | three */
7085     send_ctrl_key(hwnd, VK_RIGHT);
7086     /* one two  |three */
7087     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7088     ok(sel_start == sel_end, "Selection should be empty\n");
7089     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7090 
7091     SendMessageA(hwnd, EM_SETSEL, 11, 11);
7092     /* one two  th|ree */
7093     send_ctrl_key(hwnd, VK_LEFT);
7094     /* one two  |three */
7095     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7096     ok(sel_start == sel_end, "Selection should be empty\n");
7097     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7098 
7099     /* Test with a custom word break procedure that uses X as the delimiter. */
7100     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
7101     ok (result == TRUE, "Failed to clear the text.\n");
7102     SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7103     /* |one twoXthree */
7104     send_ctrl_key(hwnd, VK_RIGHT);
7105     /* one twoX|three */
7106     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7107     ok(sel_start == sel_end, "Selection should be empty\n");
7108     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7109 
7110     DestroyWindow(hwnd);
7111 
7112     /* Make sure the behaviour is the same with a unicode richedit window,
7113      * and using unicode functions. */
7114 
7115     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7116                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7117                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7118 
7119     /* Test with a custom word break procedure that uses X as the delimiter. */
7120     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7121     ok (result == TRUE, "Failed to clear the text.\n");
7122     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7123     /* |one twoXthree */
7124     send_ctrl_key(hwnd, VK_RIGHT);
7125     /* one twoX|three */
7126     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7127     ok(sel_start == sel_end, "Selection should be empty\n");
7128     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7129 
7130     DestroyWindow(hwnd);
7131 }
7132 
7133 static void test_EM_CHARFROMPOS(void)
7134 {
7135     HWND hwnd;
7136     int result;
7137     RECT rcClient;
7138     POINTL point;
7139     point.x = 0;
7140     point.y = 40;
7141 
7142     /* multi-line control inserts CR normally */
7143     hwnd = new_richedit(NULL);
7144     result = SendMessageA(hwnd, WM_SETTEXT, 0,
7145                           (LPARAM)"one two three four five six seven\reight");
7146     ok(result == 1, "Expected 1, got %d\n", result);
7147     GetClientRect(hwnd, &rcClient);
7148 
7149     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7150     ok(result == 34, "expected character index of 34 but got %d\n", result);
7151 
7152     /* Test with points outside the bounds of the richedit control. */
7153     point.x = -1;
7154     point.y = 40;
7155     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7156     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7157 
7158     point.x = 1000;
7159     point.y = 0;
7160     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7161     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
7162 
7163     point.x = 1000;
7164     point.y = 36;
7165     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7166     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7167 
7168     point.x = 1000;
7169     point.y = -1;
7170     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7171     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7172 
7173     point.x = 1000;
7174     point.y = rcClient.bottom + 1;
7175     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7176     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7177 
7178     point.x = 1000;
7179     point.y = rcClient.bottom;
7180     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7181     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7182 
7183     DestroyWindow(hwnd);
7184 }
7185 
7186 static void test_word_wrap(void)
7187 {
7188     HWND hwnd;
7189     POINTL point = {0, 60}; /* This point must be below the first line */
7190     const char *text = "Must be long enough to test line wrapping";
7191     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7192     int res, pos, lines;
7193 
7194     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7195      * when specified on window creation and set later. */
7196     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7197                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7198     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7199     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7200     ok(res, "WM_SETTEXT failed.\n");
7201     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7202     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7203     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7204     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7205 
7206     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
7207     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7208     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7209     DestroyWindow(hwnd);
7210 
7211     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7212                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7213     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7214 
7215     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7216     ok(res, "WM_SETTEXT failed.\n");
7217     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7218     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7219     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7220     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7221 
7222     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7223     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7224     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7225     DestroyWindow(hwnd);
7226 
7227     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7228                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7229     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7230     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7231     ok(res, "WM_SETTEXT failed.\n");
7232     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7233     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7234 
7235     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7236     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7237     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7238     DestroyWindow(hwnd);
7239 
7240     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7241                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7242                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7243     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7244     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7245     ok(res, "WM_SETTEXT failed.\n");
7246     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7247     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7248 
7249     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7250     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7251     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7252 
7253     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7254     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7255     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7256     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7257     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7258 
7259     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7260     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7261     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7262     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7263     DestroyWindow(hwnd);
7264 
7265     /* Test to see if wrapping happens with redraw disabled. */
7266     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7267                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7268     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7269     SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7270     res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7271     ok(res, "EM_REPLACESEL failed.\n");
7272     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7273     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7274     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7275     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7276     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7277 
7278     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7279     DestroyWindow(hwnd);
7280 }
7281 
7282 static void test_autoscroll(void)
7283 {
7284     HWND hwnd = new_richedit(NULL);
7285     int lines, ret, redraw;
7286     POINT pt;
7287 
7288     for (redraw = 0; redraw <= 1; redraw++) {
7289         trace("testing with WM_SETREDRAW=%d\n", redraw);
7290         SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7291         SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7292         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7293         ok(lines == 8, "%d lines instead of 8\n", lines);
7294         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7295         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7296         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7297         ret = GetWindowLongA(hwnd, GWL_STYLE);
7298         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7299 
7300         SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7301         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7302         ok(lines == 1, "%d lines instead of 1\n", lines);
7303         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7304         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7305         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7306         ret = GetWindowLongA(hwnd, GWL_STYLE);
7307         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7308     }
7309 
7310     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7311     DestroyWindow(hwnd);
7312 
7313     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7314      * auto vertical/horizontal scrolling options. */
7315     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7316                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7317                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7318     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7319     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7320     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7321     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7322     ret = GetWindowLongA(hwnd, GWL_STYLE);
7323     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7324     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7325     DestroyWindow(hwnd);
7326 
7327     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7328                           WS_POPUP|ES_MULTILINE,
7329                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7330     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7331     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7332     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7333     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7334     ret = GetWindowLongA(hwnd, GWL_STYLE);
7335     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7336     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7337     DestroyWindow(hwnd);
7338 }
7339 
7340 
7341 static void test_format_rect(void)
7342 {
7343     HWND hwnd;
7344     RECT rc, expected, clientRect;
7345     int n;
7346     DWORD options;
7347 
7348     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7349                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7350                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7351     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7352 
7353     GetClientRect(hwnd, &clientRect);
7354 
7355     expected = clientRect;
7356     InflateRect(&expected, -1, 0);
7357     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7358     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7359        wine_dbgstr_rect(&expected));
7360 
7361     for (n = -3; n <= 3; n++)
7362     {
7363       rc = clientRect;
7364       InflateRect(&rc, -n, -n);
7365       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7366 
7367       expected = rc;
7368       expected.top = max(0, rc.top);
7369       expected.left = max(0, rc.left);
7370       expected.bottom = min(clientRect.bottom, rc.bottom);
7371       expected.right = min(clientRect.right, rc.right);
7372       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7373       ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7374          wine_dbgstr_rect(&expected));
7375     }
7376 
7377     rc = clientRect;
7378     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7379     expected = clientRect;
7380     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7381     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7382        wine_dbgstr_rect(&expected));
7383 
7384     /* Adding the selectionbar adds the selectionbar width to the left side. */
7385     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7386     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7387     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7388     expected.left += 8; /* selection bar width */
7389     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7390     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7391        wine_dbgstr_rect(&expected));
7392 
7393     rc = clientRect;
7394     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7395     expected = clientRect;
7396     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7397     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7398        wine_dbgstr_rect(&expected));
7399 
7400     /* Removing the selectionbar subtracts the selectionbar width from the left side,
7401      * even if the left side is already 0. */
7402     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7403     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7404     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7405     expected.left -= 8; /* selection bar width */
7406     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7407     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7408        wine_dbgstr_rect(&expected));
7409 
7410     /* Set the absolute value of the formatting rectangle. */
7411     rc = clientRect;
7412     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7413     expected = clientRect;
7414     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7415     ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7416        wine_dbgstr_rect(&expected));
7417 
7418     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7419      * LPARAM as being a relative offset when the WPARAM value is 1, but these
7420      * tests show that this isn't true. */
7421     rc.top = 15;
7422     rc.left = 15;
7423     rc.bottom = clientRect.bottom - 15;
7424     rc.right = clientRect.right - 15;
7425     expected = rc;
7426     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7427     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7428     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7429        wine_dbgstr_rect(&expected));
7430 
7431     /* For some reason it does not limit the values to the client rect with
7432      * a WPARAM value of 1. */
7433     rc.top = -15;
7434     rc.left = -15;
7435     rc.bottom = clientRect.bottom + 15;
7436     rc.right = clientRect.right + 15;
7437     expected = rc;
7438     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7439     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7440     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7441        wine_dbgstr_rect(&expected));
7442 
7443     /* Reset to default rect and check how the format rect adjusts to window
7444      * resize and how it copes with very small windows */
7445     SendMessageA(hwnd, EM_SETRECT, 0, 0);
7446 
7447     MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7448     GetClientRect(hwnd, &clientRect);
7449 
7450     expected = clientRect;
7451     InflateRect(&expected, -1, 0);
7452     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7453     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7454        wine_dbgstr_rect(&expected));
7455 
7456     MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7457     GetClientRect(hwnd, &clientRect);
7458 
7459     expected = clientRect;
7460     InflateRect(&expected, -1, 0);
7461     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7462     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7463        wine_dbgstr_rect(&expected));
7464 
7465     MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7466     GetClientRect(hwnd, &clientRect);
7467 
7468     expected = clientRect;
7469     InflateRect(&expected, -1, 0);
7470     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7471     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7472        wine_dbgstr_rect(&expected));
7473 
7474     DestroyWindow(hwnd);
7475 
7476     /* The extended window style affects the formatting rectangle. */
7477     hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7478                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7479                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7480     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7481 
7482     GetClientRect(hwnd, &clientRect);
7483 
7484     expected = clientRect;
7485     expected.top += 1;
7486     InflateRect(&expected, -1, 0);
7487     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7488     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7489        wine_dbgstr_rect(&expected));
7490 
7491     rc = clientRect;
7492     InflateRect(&rc, -5, -5);
7493     expected = rc;
7494     expected.top -= 1;
7495     InflateRect(&expected, 1, 0);
7496     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7497     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7498     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7499        wine_dbgstr_rect(&expected));
7500 
7501     DestroyWindow(hwnd);
7502 }
7503 
7504 static void test_WM_GETDLGCODE(void)
7505 {
7506     HWND hwnd;
7507     UINT res, expected;
7508     MSG msg;
7509 
7510     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7511 
7512     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7513                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7514                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7515     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7516     msg.hwnd = hwnd;
7517     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7518     expected = expected | DLGC_WANTMESSAGE;
7519     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7520        res, expected);
7521     DestroyWindow(hwnd);
7522 
7523     msg.message = WM_KEYDOWN;
7524     msg.wParam = VK_RETURN;
7525     msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7526     msg.pt.x = 0;
7527     msg.pt.y = 0;
7528     msg.time = GetTickCount();
7529 
7530     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7531                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7532                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7533     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7534     msg.hwnd = hwnd;
7535     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7536     expected = expected | DLGC_WANTMESSAGE;
7537     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7538        res, expected);
7539     DestroyWindow(hwnd);
7540 
7541     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7542                           ES_MULTILINE|WS_POPUP,
7543                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7544     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7545     msg.hwnd = hwnd;
7546     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7547     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7548     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7549        res, expected);
7550     DestroyWindow(hwnd);
7551 
7552     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7553                           ES_WANTRETURN|WS_POPUP,
7554                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7555     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7556     msg.hwnd = hwnd;
7557     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7558     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7559     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7560        res, expected);
7561     DestroyWindow(hwnd);
7562 
7563     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7564                           WS_POPUP,
7565                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7566     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7567     msg.hwnd = hwnd;
7568     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7569     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7570     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7571        res, expected);
7572     DestroyWindow(hwnd);
7573 
7574     msg.wParam = VK_TAB;
7575     msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7576 
7577     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7578                           ES_MULTILINE|WS_POPUP,
7579                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7580     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7581     msg.hwnd = hwnd;
7582     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7583     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7584     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7585        res, expected);
7586     DestroyWindow(hwnd);
7587 
7588     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7589                           WS_POPUP,
7590                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7591     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7592     msg.hwnd = hwnd;
7593     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7594     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7595     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7596        res, expected);
7597     DestroyWindow(hwnd);
7598 
7599     hold_key(VK_CONTROL);
7600 
7601     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7602                           ES_MULTILINE|WS_POPUP,
7603                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7604     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7605     msg.hwnd = hwnd;
7606     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7607     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7608     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7609        res, expected);
7610     DestroyWindow(hwnd);
7611 
7612     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7613                           WS_POPUP,
7614                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7615     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7616     msg.hwnd = hwnd;
7617     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7618     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7619     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7620        res, expected);
7621     DestroyWindow(hwnd);
7622 
7623     release_key(VK_CONTROL);
7624 
7625     msg.wParam = 'a';
7626     msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7627 
7628     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7629                           ES_MULTILINE|WS_POPUP,
7630                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7631     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7632     msg.hwnd = hwnd;
7633     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7634     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7635     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7636        res, expected);
7637     DestroyWindow(hwnd);
7638 
7639     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7640                           WS_POPUP,
7641                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7642     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7643     msg.hwnd = hwnd;
7644     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7645     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7646     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7647        res, expected);
7648     DestroyWindow(hwnd);
7649 
7650     msg.message = WM_CHAR;
7651 
7652     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7653                           ES_MULTILINE|WS_POPUP,
7654                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7655     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7656     msg.hwnd = hwnd;
7657     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7658     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7659     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7660        res, expected);
7661     DestroyWindow(hwnd);
7662 
7663     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7664                           WS_POPUP,
7665                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7666     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7667     msg.hwnd = hwnd;
7668     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7669     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7670     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7671        res, expected);
7672     DestroyWindow(hwnd);
7673 
7674     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7675                           WS_POPUP|ES_SAVESEL,
7676                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7677     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7678     res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7679     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7680     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7681        res, expected);
7682     DestroyWindow(hwnd);
7683 }
7684 
7685 static void test_zoom(void)
7686 {
7687     HWND hwnd;
7688     UINT ret;
7689     RECT rc;
7690     POINT pt;
7691     int numerator, denominator;
7692 
7693     hwnd = new_richedit(NULL);
7694     GetClientRect(hwnd, &rc);
7695     pt.x = (rc.right - rc.left) / 2;
7696     pt.y = (rc.bottom - rc.top) / 2;
7697     ClientToScreen(hwnd, &pt);
7698 
7699     /* Test initial zoom value */
7700     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7701     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7702     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7703     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7704 
7705     /* test scroll wheel */
7706     hold_key(VK_CONTROL);
7707     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7708                       MAKELPARAM(pt.x, pt.y));
7709     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7710     release_key(VK_CONTROL);
7711 
7712     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7713     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7714     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7715     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7716 
7717     /* Test how much the mouse wheel can zoom in and out. */
7718     ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7719     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7720 
7721     hold_key(VK_CONTROL);
7722     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7723                       MAKELPARAM(pt.x, pt.y));
7724     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7725     release_key(VK_CONTROL);
7726 
7727     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7728     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7729     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7730     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7731 
7732     ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7733     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7734 
7735     hold_key(VK_CONTROL);
7736     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7737                       MAKELPARAM(pt.x, pt.y));
7738     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7739     release_key(VK_CONTROL);
7740 
7741     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7742     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7743     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7744     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7745 
7746     ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7747     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7748 
7749     hold_key(VK_CONTROL);
7750     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7751                       MAKELPARAM(pt.x, pt.y));
7752     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7753     release_key(VK_CONTROL);
7754 
7755     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7756     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7757     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7758     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7759 
7760     ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7761     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7762 
7763     hold_key(VK_CONTROL);
7764     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7765                       MAKELPARAM(pt.x, pt.y));
7766     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7767     release_key(VK_CONTROL);
7768 
7769     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7770     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7771     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7772     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7773 
7774     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7775     ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7776     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7777 
7778     hold_key(VK_CONTROL);
7779     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7780                       MAKELPARAM(pt.x, pt.y));
7781     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7782     release_key(VK_CONTROL);
7783 
7784     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7785     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7786     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7787     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7788 
7789     /* Test bounds checking on EM_SETZOOM */
7790     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7791     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7792 
7793     ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7794     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7795 
7796     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7797     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7798 
7799     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7800     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7801     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7802     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7803 
7804     ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7805     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7806 
7807     /* See if negative numbers are accepted. */
7808     ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7809     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7810 
7811     /* See if negative numbers are accepted. */
7812     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7813     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7814 
7815     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7816     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7817     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7818     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7819 
7820     /* Reset the zoom value */
7821     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7822     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7823 
7824     DestroyWindow(hwnd);
7825 }
7826 
7827 struct dialog_mode_messages
7828 {
7829     int wm_getdefid, wm_close, wm_nextdlgctl;
7830 };
7831 
7832 static struct dialog_mode_messages dm_messages;
7833 
7834 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7835     ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7836     "got %d\n", wmclose, dm_messages.wm_close); \
7837     ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7838     "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7839     ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7840     "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7841 
7842 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7843 {
7844     switch (iMsg)
7845     {
7846         case DM_GETDEFID:
7847             dm_messages.wm_getdefid++;
7848             return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7849         case WM_NEXTDLGCTL:
7850             dm_messages.wm_nextdlgctl++;
7851             break;
7852         case WM_CLOSE:
7853             dm_messages.wm_close++;
7854             break;
7855     }
7856 
7857     return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7858 }
7859 
7860 static void test_dialogmode(void)
7861 {
7862     HWND hwRichEdit, hwParent, hwButton;
7863     MSG msg= {0};
7864     int lcount, r;
7865     WNDCLASSA cls;
7866 
7867     cls.style = 0;
7868     cls.lpfnWndProc = dialog_mode_wnd_proc;
7869     cls.cbClsExtra = 0;
7870     cls.cbWndExtra = 0;
7871     cls.hInstance = GetModuleHandleA(0);
7872     cls.hIcon = 0;
7873     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7874     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7875     cls.lpszMenuName = NULL;
7876     cls.lpszClassName = "DialogModeParentClass";
7877     if(!RegisterClassA(&cls)) assert(0);
7878 
7879     hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7880       CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7881 
7882     /* Test richedit(ES_MULTILINE) */
7883 
7884     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7885 
7886     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7887     ok(0 == r, "expected 0, got %d\n", r);
7888     lcount = SendMessageA(hwRichEdit,  EM_GETLINECOUNT, 0, 0);
7889     ok(2 == lcount, "expected 2, got %d\n", lcount);
7890 
7891     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7892     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7893 
7894     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7895     ok(0 == r, "expected 0, got %d\n", r);
7896     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7897     ok(3 == lcount, "expected 3, got %d\n", lcount);
7898 
7899     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7900     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7901     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7902     ok(0 == r, "expected 0, got %d\n", r);
7903     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7904     ok(3 == lcount, "expected 3, got %d\n", lcount);
7905 
7906     DestroyWindow(hwRichEdit);
7907 
7908     /* Test standalone richedit(ES_MULTILINE) */
7909 
7910     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7911 
7912     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7913     ok(0 == r, "expected 0, got %d\n", r);
7914     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7915     ok(2 == lcount, "expected 2, got %d\n", lcount);
7916 
7917     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7918     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7919 
7920     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7921     ok(0 == r, "expected 0, got %d\n", r);
7922     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7923     ok(2 == lcount, "expected 2, got %d\n", lcount);
7924 
7925     DestroyWindow(hwRichEdit);
7926 
7927     /* Check  a destination for messages */
7928 
7929     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7930 
7931     SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7932     SetParent( hwRichEdit, NULL);
7933 
7934     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7935     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7936 
7937     memset(&dm_messages, 0, sizeof(dm_messages));
7938     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7939     ok(0 == r, "expected 0, got %d\n", r);
7940     test_dm_messages(0, 1, 0);
7941 
7942     memset(&dm_messages, 0, sizeof(dm_messages));
7943     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7944     ok(0 == r, "expected 0, got %d\n", r);
7945     test_dm_messages(0, 0, 1);
7946 
7947     DestroyWindow(hwRichEdit);
7948 
7949     /* Check messages from richedit(ES_MULTILINE) */
7950 
7951     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7952 
7953     memset(&dm_messages, 0, sizeof(dm_messages));
7954     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7955     ok(0 == r, "expected 0, got %d\n", r);
7956     test_dm_messages(0, 0, 0);
7957 
7958     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7959     ok(2 == lcount, "expected 2, got %d\n", lcount);
7960 
7961     memset(&dm_messages, 0, sizeof(dm_messages));
7962     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7963     ok(0 == r, "expected 0, got %d\n", r);
7964     test_dm_messages(0, 0, 0);
7965 
7966     memset(&dm_messages, 0, sizeof(dm_messages));
7967     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7968     ok(0 == r, "expected 0, got %d\n", r);
7969     test_dm_messages(0, 0, 0);
7970 
7971     memset(&dm_messages, 0, sizeof(dm_messages));
7972     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7973     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7974     test_dm_messages(0, 0, 0);
7975 
7976     memset(&dm_messages, 0, sizeof(dm_messages));
7977     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7978     ok(0 == r, "expected 0, got %d\n", r);
7979     test_dm_messages(0, 1, 0);
7980 
7981     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7982     ok(2 == lcount, "expected 2, got %d\n", lcount);
7983 
7984     memset(&dm_messages, 0, sizeof(dm_messages));
7985     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7986     ok(0 == r, "expected 0, got %d\n", r);
7987     test_dm_messages(0, 0, 0);
7988 
7989     memset(&dm_messages, 0, sizeof(dm_messages));
7990     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7991     ok(0 == r, "expected 0, got %d\n", r);
7992     test_dm_messages(0, 0, 1);
7993 
7994     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7995         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7996     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7997 
7998     memset(&dm_messages, 0, sizeof(dm_messages));
7999     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8000     ok(0 == r, "expected 0, got %d\n", r);
8001     test_dm_messages(0, 1, 1);
8002 
8003     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8004     ok(2 == lcount, "expected 2, got %d\n", lcount);
8005 
8006     DestroyWindow(hwButton);
8007     DestroyWindow(hwRichEdit);
8008 
8009     /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
8010 
8011     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
8012 
8013     memset(&dm_messages, 0, sizeof(dm_messages));
8014     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8015     ok(0 == r, "expected 0, got %d\n", r);
8016     test_dm_messages(0, 0, 0);
8017 
8018     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8019     ok(2 == lcount, "expected 2, got %d\n", lcount);
8020 
8021     memset(&dm_messages, 0, sizeof(dm_messages));
8022     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8023     ok(0 == r, "expected 0, got %d\n", r);
8024     test_dm_messages(0, 0, 0);
8025 
8026     memset(&dm_messages, 0, sizeof(dm_messages));
8027     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8028     ok(0 == r, "expected 0, got %d\n", r);
8029     test_dm_messages(0, 0, 0);
8030 
8031     memset(&dm_messages, 0, sizeof(dm_messages));
8032     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8033     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8034     test_dm_messages(0, 0, 0);
8035 
8036     memset(&dm_messages, 0, sizeof(dm_messages));
8037     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8038     ok(0 == r, "expected 0, got %d\n", r);
8039     test_dm_messages(0, 0, 0);
8040 
8041     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8042     ok(3 == lcount, "expected 3, got %d\n", lcount);
8043 
8044     memset(&dm_messages, 0, sizeof(dm_messages));
8045     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8046     ok(0 == r, "expected 0, got %d\n", r);
8047     test_dm_messages(0, 0, 0);
8048 
8049     memset(&dm_messages, 0, sizeof(dm_messages));
8050     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8051     ok(0 == r, "expected 0, got %d\n", r);
8052     test_dm_messages(0, 0, 1);
8053 
8054     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8055         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8056     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8057 
8058     memset(&dm_messages, 0, sizeof(dm_messages));
8059     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8060     ok(0 == r, "expected 0, got %d\n", r);
8061     test_dm_messages(0, 0, 0);
8062 
8063     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8064     ok(4 == lcount, "expected 4, got %d\n", lcount);
8065 
8066     DestroyWindow(hwButton);
8067     DestroyWindow(hwRichEdit);
8068 
8069     /* Check messages from richedit(0) */
8070 
8071     hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
8072 
8073     memset(&dm_messages, 0, sizeof(dm_messages));
8074     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8075     ok(0 == r, "expected 0, got %d\n", r);
8076     test_dm_messages(0, 0, 0);
8077 
8078     memset(&dm_messages, 0, sizeof(dm_messages));
8079     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8080     ok(0 == r, "expected 0, got %d\n", r);
8081     test_dm_messages(0, 0, 0);
8082 
8083     memset(&dm_messages, 0, sizeof(dm_messages));
8084     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8085     ok(0 == r, "expected 0, got %d\n", r);
8086     test_dm_messages(0, 0, 0);
8087 
8088     memset(&dm_messages, 0, sizeof(dm_messages));
8089     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8090     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8091     test_dm_messages(0, 0, 0);
8092 
8093     memset(&dm_messages, 0, sizeof(dm_messages));
8094     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8095     ok(0 == r, "expected 0, got %d\n", r);
8096     test_dm_messages(0, 1, 0);
8097 
8098     memset(&dm_messages, 0, sizeof(dm_messages));
8099     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8100     ok(0 == r, "expected 0, got %d\n", r);
8101     test_dm_messages(0, 0, 0);
8102 
8103     memset(&dm_messages, 0, sizeof(dm_messages));
8104     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8105     ok(0 == r, "expected 0, got %d\n", r);
8106     test_dm_messages(0, 0, 1);
8107 
8108     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8109         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8110     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8111 
8112     memset(&dm_messages, 0, sizeof(dm_messages));
8113     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8114     ok(0 == r, "expected 0, got %d\n", r);
8115     test_dm_messages(0, 1, 1);
8116 
8117     DestroyWindow(hwRichEdit);
8118 
8119     /* Check messages from richedit(ES_WANTRETURN) */
8120 
8121     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8122 
8123     memset(&dm_messages, 0, sizeof(dm_messages));
8124     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8125     ok(0 == r, "expected 0, got %d\n", r);
8126     test_dm_messages(0, 0, 0);
8127 
8128     memset(&dm_messages, 0, sizeof(dm_messages));
8129     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8130     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8131     test_dm_messages(0, 0, 0);
8132 
8133     memset(&dm_messages, 0, sizeof(dm_messages));
8134     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8135     ok(0 == r, "expected 0, got %d\n", r);
8136     test_dm_messages(0, 0, 0);
8137 
8138     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8139         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8140     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8141 
8142     memset(&dm_messages, 0, sizeof(dm_messages));
8143     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8144     ok(0 == r, "expected 0, got %d\n", r);
8145     test_dm_messages(0, 0, 0);
8146 
8147     DestroyWindow(hwRichEdit);
8148     DestroyWindow(hwParent);
8149 }
8150 
8151 static void test_EM_FINDWORDBREAK_W(void)
8152 {
8153     static const struct {
8154         WCHAR c;
8155         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8156     } delimiter_tests[] = {
8157         {0x0a,   FALSE},         /* newline */
8158         {0x0b,   FALSE},         /* vertical tab */
8159         {0x0c,   FALSE},         /* form feed */
8160         {0x0d,   FALSE},         /* carriage return */
8161         {0x20,   TRUE},          /* space */
8162         {0x61,   FALSE},         /* capital letter a */
8163         {0xa0,   FALSE},         /* no-break space */
8164         {0x2000, FALSE},         /* en quad */
8165         {0x3000, FALSE},         /* Ideographic space */
8166         {0x1100, FALSE},         /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8167         {0x11ff, FALSE},         /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8168         {0x115f, FALSE},         /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8169         {0xac00, FALSE},         /* Hangul character GA*/
8170         {0xd7af, FALSE},         /* End of Hangul character chart */
8171         {0xf020, TRUE},          /* MS private for CP_SYMBOL round trip?, see kb897872 */
8172         {0xff20, FALSE},         /* fullwidth commercial @ */
8173         {WCH_EMBEDDING, FALSE},  /* object replacement character*/
8174     };
8175     int i;
8176     HWND hwndRichEdit = new_richeditW(NULL);
8177     ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8178     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8179     {
8180         WCHAR wbuf[2];
8181         int result;
8182 
8183         wbuf[0] = delimiter_tests[i].c;
8184         wbuf[1] = 0;
8185         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8186         result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8187         todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8188             ok(result == delimiter_tests[i].isdelimiter,
8189                 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8190                 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8191     }
8192     DestroyWindow(hwndRichEdit);
8193 }
8194 
8195 static void test_EM_FINDWORDBREAK_A(void)
8196 {
8197     static const struct {
8198         WCHAR c;
8199         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8200     } delimiter_tests[] = {
8201         {0x0a,   FALSE},         /* newline */
8202         {0x0b,   FALSE},         /* vertical tab */
8203         {0x0c,   FALSE},         /* form feed */
8204         {0x0d,   FALSE},         /* carriage return */
8205         {0x20,   TRUE},          /* space */
8206         {0x61,   FALSE},         /* capital letter a */
8207     };
8208     int i;
8209     HWND hwndRichEdit = new_richedit(NULL);
8210 
8211     ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8212     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
8213     {
8214         int result;
8215         char buf[2];
8216         buf[0] = delimiter_tests[i].c;
8217         buf[1] = 0;
8218         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8219         result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8220         todo_wine_if (buf[0] == 0x20)
8221             ok(result == delimiter_tests[i].isdelimiter,
8222                "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8223                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8224     }
8225     DestroyWindow(hwndRichEdit);
8226 }
8227 
8228 /*
8229  * This test attempts to show the effect of enter on a richedit
8230  * control v1.0 inserts CRLF whereas for higher versions it only
8231  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8232  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8233  * does for higher. The same test is cloned in riched32 and riched20.
8234  */
8235 static void test_enter(void)
8236 {
8237     static const struct {
8238       const char *initialtext;
8239       const int   cursor;
8240       const char *expectedwmtext;
8241       const char *expectedemtext;
8242       const char *expectedemtextcrlf;
8243     } testenteritems[] = {
8244       { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8245       { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8246       { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8247       { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8248       { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8249     };
8250 
8251   char expectedbuf[1024];
8252   char resultbuf[1024];
8253   HWND hwndRichEdit = new_richedit(NULL);
8254   UINT i,j;
8255 
8256   for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
8257 
8258     char buf[1024] = {0};
8259     LRESULT result;
8260     GETTEXTEX getText;
8261     const char *expected;
8262 
8263     /* Set the text to the initial text */
8264     result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8265     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8266 
8267     /* Send Enter */
8268     SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8269     simulate_typing_characters(hwndRichEdit, "\r");
8270 
8271     /* 1. Retrieve with WM_GETTEXT */
8272     buf[0] = 0x00;
8273     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8274     expected = testenteritems[i].expectedwmtext;
8275 
8276     resultbuf[0]=0x00;
8277     for (j = 0; j < (UINT)result; j++)
8278       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8279     expectedbuf[0] = '\0';
8280     for (j = 0; j < strlen(expected); j++)
8281       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8282 
8283     result = strcmp(expected, buf);
8284     ok (result == 0,
8285         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8286         i, resultbuf, expectedbuf);
8287 
8288     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8289     getText.cb = sizeof(buf);
8290     getText.flags = GT_DEFAULT;
8291     getText.codepage      = CP_ACP;
8292     getText.lpDefaultChar = NULL;
8293     getText.lpUsedDefChar = NULL;
8294     buf[0] = 0x00;
8295     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8296     expected = testenteritems[i].expectedemtext;
8297 
8298     resultbuf[0]=0x00;
8299     for (j = 0; j < (UINT)result; j++)
8300       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8301     expectedbuf[0] = '\0';
8302     for (j = 0; j < strlen(expected); j++)
8303       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8304 
8305     result = strcmp(expected, buf);
8306     ok (result == 0,
8307         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8308         i, resultbuf, expectedbuf);
8309 
8310     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8311     getText.cb = sizeof(buf);
8312     getText.flags = GT_USECRLF;
8313     getText.codepage      = CP_ACP;
8314     getText.lpDefaultChar = NULL;
8315     getText.lpUsedDefChar = NULL;
8316     buf[0] = 0x00;
8317     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8318     expected = testenteritems[i].expectedemtextcrlf;
8319 
8320     resultbuf[0]=0x00;
8321     for (j = 0; j < (UINT)result; j++)
8322       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8323     expectedbuf[0] = '\0';
8324     for (j = 0; j < strlen(expected); j++)
8325       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8326 
8327     result = strcmp(expected, buf);
8328     ok (result == 0,
8329         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8330         i, resultbuf, expectedbuf);
8331   }
8332 
8333   DestroyWindow(hwndRichEdit);
8334 }
8335 
8336 static void test_WM_CREATE(void)
8337 {
8338     static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8339     static const char title[] = "line1\nline2";
8340 
8341     HWND rich_edit;
8342     LRESULT res;
8343     char buf[64];
8344     int len;
8345 
8346     rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8347             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8348     ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8349 
8350     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8351     ok(len == 5, "GetWindowText returned %d\n", len);
8352     ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8353 
8354     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8355     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8356 
8357     DestroyWindow(rich_edit);
8358 
8359     rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8360             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8361     ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8362 
8363     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8364     ok(len == 12, "GetWindowText returned %d\n", len);
8365     ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8366 
8367     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8368     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8369 
8370     DestroyWindow(rich_edit);
8371 }
8372 
8373 /*******************************************************************
8374  * Test that after deleting all of the text, the first paragraph
8375  * format reverts to the default.
8376  */
8377 static void test_reset_default_para_fmt( void )
8378 {
8379     HWND richedit = new_richeditW( NULL );
8380     PARAFORMAT2 fmt;
8381     WORD def_align, new_align;
8382 
8383     memset( &fmt, 0, sizeof(fmt) );
8384     fmt.cbSize = sizeof(PARAFORMAT2);
8385     fmt.dwMask = -1;
8386     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8387     def_align = fmt.wAlignment;
8388     new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8389 
8390     simulate_typing_characters( richedit, "123" );
8391 
8392     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8393     fmt.dwMask = PFM_ALIGNMENT;
8394     fmt.wAlignment = new_align;
8395     SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8396 
8397     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8398     ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8399 
8400     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8401     SendMessageA( richedit, WM_CUT, 0, 0 );
8402 
8403     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8404     ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8405 
8406     DestroyWindow( richedit );
8407 }
8408 
8409 static void test_EM_SETREADONLY(void)
8410 {
8411     HWND richedit = new_richeditW(NULL);
8412     DWORD dwStyle;
8413     LRESULT res;
8414 
8415     res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8416     ok(res == 1, "EM_SETREADONLY\n");
8417     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8418     ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8419 
8420     res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8421     ok(res == 1, "EM_SETREADONLY\n");
8422     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8423     ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8424 
8425     DestroyWindow(richedit);
8426 }
8427 
8428 static inline LONG twips2points(LONG value)
8429 {
8430     return value / 20;
8431 }
8432 
8433 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8434     _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8435 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8436                             LRESULT expected_res, BOOL expected_undo)
8437 {
8438     CHARFORMAT2A cf;
8439     LRESULT res;
8440     BOOL isundo;
8441 
8442     cf.cbSize = sizeof(cf);
8443     cf.dwMask = CFM_SIZE;
8444 
8445     res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8446     SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8447     isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8448     ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8449     ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8450                        twips2points(cf.yHeight), expected_size);
8451     ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8452                        isundo, expected_undo);
8453 }
8454 
8455 static void test_EM_SETFONTSIZE(void)
8456 {
8457     HWND richedit = new_richedit(NULL);
8458     CHAR text[] = "wine";
8459     CHARFORMAT2A tmp_cf;
8460     LONG default_size;
8461 
8462     tmp_cf.cbSize = sizeof(tmp_cf);
8463     tmp_cf.dwMask = CFM_SIZE;
8464     tmp_cf.yHeight = 9 * 20.0;
8465     SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8466 
8467     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8468 
8469     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8470     /* without selection */
8471     TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8472     SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8473     default_size = twips2points(tmp_cf.yHeight);
8474     ok(default_size == 9, "Default font size should not be changed.\n");
8475     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8476 
8477     SendMessageA(richedit, EM_SETSEL, 0, 2);
8478 
8479     TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8480 
8481     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8482     TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8483     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8484 
8485     TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8486     TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8487     TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8488     TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8489     TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8490     TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8491     TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8492     TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8493     TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8494     TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8495     TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8496     TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8497     TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8498     TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8499     TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8500     TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8501     TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8502     TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8503     TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8504     TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8505     TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8506     TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8507     TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8508     TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8509     TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8510     TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8511     TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8512     TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8513 
8514     /* return FALSE when richedit is TM_PLAINTEXT mode */
8515     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8516     SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8517     TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8518 
8519     DestroyWindow(richedit);
8520 }
8521 
8522 static void test_alignment_style(void)
8523 {
8524     HWND richedit = NULL;
8525     PARAFORMAT2 pf;
8526     DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8527                            ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8528                            ES_LEFT | ES_RIGHT | ES_CENTER};
8529     DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8530                           PFA_RIGHT, PFA_CENTER};
8531     const char * streamtext =
8532         "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8533         "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8534         "}\r\n";
8535     EDITSTREAM es;
8536     int i;
8537 
8538     for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8539     {
8540         DWORD dwStyle, new_align;
8541 
8542         richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8543         memset(&pf, 0, sizeof(pf));
8544         pf.cbSize = sizeof(PARAFORMAT2);
8545         pf.dwMask = -1;
8546 
8547         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8548         ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8549            i, pf.wAlignment, align_mask[i]);
8550         dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8551         ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8552            "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8553 
8554 
8555         /* Based on test_reset_default_para_fmt() */
8556         new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8557         simulate_typing_characters(richedit, "123");
8558 
8559         SendMessageW(richedit, EM_SETSEL, 0, -1);
8560         pf.dwMask = PFM_ALIGNMENT;
8561         pf.wAlignment = new_align;
8562         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8563 
8564         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8565         ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8566 
8567         SendMessageW(richedit, EM_SETSEL, 0, -1);
8568         SendMessageW(richedit, WM_CUT, 0, 0);
8569 
8570         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8571         ok(pf.wAlignment == align_mask[i], "got %d expect %d\n", pf.wAlignment, align_mask[i]);
8572 
8573         DestroyWindow(richedit);
8574     }
8575 
8576     /* test with EM_STREAMIN */
8577     richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8578     simulate_typing_characters(richedit, "abc");
8579     es.dwCookie = (DWORD_PTR)&streamtext;
8580     es.dwError = 0;
8581     es.pfnCallback = test_EM_STREAMIN_esCallback;
8582     SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8583     SendMessageW(richedit, EM_SETSEL, 0, -1);
8584     memset(&pf, 0, sizeof(pf));
8585     pf.cbSize = sizeof(PARAFORMAT2);
8586     pf.dwMask = -1;
8587     SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8588     ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8589     DestroyWindow(richedit);
8590 }
8591 
8592 static void test_WM_GETTEXTLENGTH(void)
8593 {
8594     HWND hwndRichEdit = new_richedit(NULL);
8595     static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8596     static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8597     static const char text3[] = "abcdef\x8e\xf0";
8598     int result;
8599 
8600     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8601     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8602     ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8603        result, lstrlenA(text1));
8604 
8605     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8606     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8607     ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8608        result, lstrlenA(text2));
8609 
8610     /* Test with multibyte character */
8611     if (!is_lang_japanese)
8612         skip("Skip multibyte character tests on non-Japanese platform\n");
8613     else
8614     {
8615         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8616         result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8617         todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8618     }
8619 
8620     DestroyWindow(hwndRichEdit);
8621 }
8622 
8623 static void test_rtf(void)
8624 {
8625     const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8626         "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8627     const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8628                                      0x201d,0x200e,0x200f,0x200d,0x200c};
8629     const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8630     const char *highlight = "{\\rtf1{\\colortbl;\\red0\\green0\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;}\\cf2\\highlight3 foo\\par}";
8631 
8632     HWND edit = new_richeditW( NULL );
8633     EDITSTREAM es;
8634     WCHAR buf[80];
8635     LRESULT result;
8636     PARAFORMAT2 fmt;
8637     CHARFORMAT2W cf;
8638 
8639     /* Test rtf specials */
8640     es.dwCookie = (DWORD_PTR)&specials;
8641     es.dwError = 0;
8642     es.pfnCallback = test_EM_STREAMIN_esCallback;
8643     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8644     ok( result == 11, "got %ld\n", result );
8645 
8646     result = SendMessageW( edit, WM_GETTEXT, sizeof(buf)/sizeof(buf[0]), (LPARAM)buf );
8647     ok( result == sizeof(expect_specials)/sizeof(expect_specials[0]), "got %ld\n", result );
8648     ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8649 
8650     /* Show that \rtlpar propagates to the second paragraph and is
8651        reset by \pard in the third. */
8652     es.dwCookie = (DWORD_PTR)&pard;
8653     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8654     ok( result == 11, "got %ld\n", result );
8655 
8656     fmt.cbSize = sizeof(fmt);
8657     SendMessageW( edit, EM_SETSEL, 1, 1 );
8658     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8659     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8660     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8661     SendMessageW( edit, EM_SETSEL, 5, 5 );
8662     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8663     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8664     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8665     SendMessageW( edit, EM_SETSEL, 9, 9 );
8666     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8667     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8668     ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8669 
8670     /* Test \highlight */
8671     es.dwCookie = (DWORD_PTR)&highlight;
8672     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8673     ok( result == 3, "got %ld\n", result );
8674     SendMessageW( edit, EM_SETSEL, 1, 1 );
8675     memset( &cf, 0, sizeof(cf) );
8676     cf.cbSize = sizeof(cf);
8677     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8678     ok( (cf.dwEffects & (CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR)) == 0, "got %08x\n", cf.dwEffects );
8679     ok( cf.crTextColor == RGB(128,128,128), "got %08x\n", cf.crTextColor );
8680     ok( cf.crBackColor == RGB(192,192,192), "got %08x\n", cf.crBackColor );
8681 
8682     DestroyWindow( edit );
8683 }
8684 
8685 static void test_background(void)
8686 {
8687     HWND hwndRichEdit = new_richedit(NULL);
8688 
8689     /* set the background color to black */
8690     ValidateRect(hwndRichEdit, NULL);
8691     SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8692     ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8693 
8694     DestroyWindow(hwndRichEdit);
8695 }
8696 
8697 static void test_eop_char_fmt(void)
8698 {
8699     HWND edit = new_richedit( NULL );
8700     const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8701         "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8702         "\\f0\\fs25 Second\\par"
8703         "{\\f0\\fs26 Third}\\par"
8704         "{\\f0\\fs22 Fourth}\\par}}}";
8705     EDITSTREAM es;
8706     CHARFORMAT2W cf;
8707     int i, num, expect_height;
8708 
8709     es.dwCookie = (DWORD_PTR)&rtf;
8710     es.dwError = 0;
8711     es.pfnCallback = test_EM_STREAMIN_esCallback;
8712     num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8713     ok( num == 25, "got %d\n", num );
8714 
8715     for (i = 0; i <= num; i++)
8716     {
8717         SendMessageW( edit, EM_SETSEL, i, i + 1 );
8718         cf.cbSize = sizeof(cf);
8719         cf.dwMask = CFM_SIZE;
8720         SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8721         ok( cf.dwMask & CFM_SIZE, "%d: got %08x\n", i, cf.dwMask );
8722         if (i < 6) expect_height = 160;
8723         else if (i < 13) expect_height = 250;
8724         else if (i < 18) expect_height = 260;
8725         else if (i == 18 || i == 25) expect_height = 250;
8726         else expect_height = 220;
8727         ok( cf.yHeight == expect_height, "%d: got %d\n", i, cf.yHeight );
8728     }
8729 
8730     DestroyWindow( edit );
8731 }
8732 
8733 static void test_para_numbering(void)
8734 {
8735     HWND edit = new_richeditW( NULL );
8736     const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8737         "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8738         "\\fs20\\fi200\\li360\\f0 First\\par"
8739         "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
8740         "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
8741     const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
8742     EDITSTREAM es;
8743     WCHAR buf[80];
8744     LRESULT result;
8745     PARAFORMAT2 fmt, fmt2;
8746     GETTEXTEX get_text;
8747     CHARFORMAT2W cf;
8748 
8749     get_text.cb = sizeof(buf);
8750     get_text.flags = GT_RAWTEXT;
8751     get_text.codepage = 1200;
8752     get_text.lpDefaultChar = NULL;
8753     get_text.lpUsedDefChar = NULL;
8754 
8755     es.dwCookie = (DWORD_PTR)&numbers;
8756     es.dwError = 0;
8757     es.pfnCallback = test_EM_STREAMIN_esCallback;
8758     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8759     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8760 
8761     result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
8762     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8763     ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
8764 
8765     SendMessageW( edit, EM_SETSEL, 1, 1 );
8766     memset( &fmt, 0, sizeof(fmt) );
8767     fmt.cbSize = sizeof(fmt);
8768     fmt.dwMask = PFM_ALL2;
8769     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8770     ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
8771     ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
8772     ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
8773     ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
8774     ok( fmt.dxStartIndent == 560, "got %d\n", fmt.dxStartIndent );
8775     ok( fmt.dxOffset == -200, "got %d\n", fmt.dxOffset );
8776 
8777     /* Second para should have identical fmt */
8778     SendMessageW( edit, EM_SETSEL, 10, 10 );
8779     memset( &fmt2, 0, sizeof(fmt2) );
8780     fmt2.cbSize = sizeof(fmt2);
8781     fmt2.dwMask = PFM_ALL2;
8782     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
8783     ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
8784 
8785     /* Check the eop heights - this determines the label height */
8786     SendMessageW( edit, EM_SETSEL, 12, 13 );
8787     cf.cbSize = sizeof(cf);
8788     cf.dwMask = CFM_SIZE;
8789     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8790     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8791 
8792     SendMessageW( edit, EM_SETSEL, 18, 19 );
8793     cf.cbSize = sizeof(cf);
8794     cf.dwMask = CFM_SIZE;
8795     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8796     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8797 
8798     DestroyWindow( edit );
8799 }
8800 
8801 static void test_window_classes(void)
8802 {
8803     static const struct
8804     {
8805         const char *class;
8806         BOOL success;
8807     } test[] =
8808     {
8809         { "RichEdit", FALSE },
8810         { "RichEdit20A", TRUE },
8811         { "RichEdit20W", TRUE },
8812         { "RichEdit50A", FALSE },
8813         { "RichEdit50W", FALSE }
8814     };
8815     int i;
8816     HWND hwnd;
8817 
8818     for (i = 0; i < sizeof(test)/sizeof(test[0]); i++)
8819     {
8820         SetLastError(0xdeadbeef);
8821         hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
8822 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
8823         ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
8824            test[i].class, test[i].success ? "succeed" : "fail");
8825         if (!hwnd)
8826 todo_wine
8827             ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %d\n", GetLastError());
8828         else
8829             DestroyWindow(hwnd);
8830     }
8831 }
8832 
8833 START_TEST( editor )
8834 {
8835   BOOL ret;
8836   /* Must explicitly LoadLibrary(). The test has no references to functions in
8837    * RICHED20.DLL, so the linker doesn't actually link to it. */
8838   hmoduleRichEdit = LoadLibraryA("riched20.dll");
8839   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8840   is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
8841 
8842   test_window_classes();
8843   test_WM_CHAR();
8844   test_EM_FINDTEXT(FALSE);
8845   test_EM_FINDTEXT(TRUE);
8846   test_EM_GETLINE();
8847   test_EM_POSFROMCHAR();
8848   test_EM_SCROLLCARET();
8849   test_EM_SCROLL();
8850   test_scrollbar_visibility();
8851   test_WM_SETTEXT();
8852   test_EM_LINELENGTH();
8853   test_EM_SETCHARFORMAT();
8854   test_EM_SETTEXTMODE();
8855   test_TM_PLAINTEXT();
8856   test_EM_SETOPTIONS();
8857   test_WM_GETTEXT();
8858   test_EM_GETTEXTRANGE();
8859   test_EM_GETSELTEXT();
8860   test_EM_SETUNDOLIMIT();
8861   test_ES_PASSWORD();
8862   test_EM_SETTEXTEX();
8863   test_EM_LIMITTEXT();
8864   test_EM_EXLIMITTEXT();
8865   test_EM_GETLIMITTEXT();
8866   test_WM_SETFONT();
8867   test_EM_GETMODIFY();
8868   test_EM_SETSEL();
8869   test_EM_EXSETSEL();
8870   test_WM_PASTE();
8871   test_EM_STREAMIN();
8872   test_EM_STREAMOUT();
8873   test_EM_STREAMOUT_FONTTBL();
8874   test_EM_STREAMOUT_empty_para();
8875   test_EM_StreamIn_Undo();
8876   test_EM_FORMATRANGE();
8877   test_unicode_conversions();
8878   test_EM_GETTEXTLENGTHEX();
8879   test_WM_GETTEXTLENGTH();
8880   test_EM_REPLACESEL(1);
8881   test_EM_REPLACESEL(0);
8882   test_WM_NOTIFY();
8883   test_EN_LINK();
8884   test_EM_AUTOURLDETECT();
8885   test_eventMask();
8886   test_undo_coalescing();
8887   test_word_movement();
8888   test_EM_CHARFROMPOS();
8889   test_SETPARAFORMAT();
8890   test_word_wrap();
8891   test_autoscroll();
8892   test_format_rect();
8893   test_WM_GETDLGCODE();
8894   test_zoom();
8895   test_dialogmode();
8896   test_EM_FINDWORDBREAK_W();
8897   test_EM_FINDWORDBREAK_A();
8898   test_enter();
8899   test_WM_CREATE();
8900   test_reset_default_para_fmt();
8901   test_EM_SETREADONLY();
8902   test_EM_SETFONTSIZE();
8903   test_alignment_style();
8904   test_rtf();
8905   test_background();
8906   test_eop_char_fmt();
8907   test_para_numbering();
8908 
8909   /* Set the environment variable WINETEST_RICHED20 to keep windows
8910    * responsive and open for 30 seconds. This is useful for debugging.
8911    */
8912   if (getenv( "WINETEST_RICHED20" )) {
8913     keep_responsive(30);
8914   }
8915 
8916   OleFlushClipboard();
8917   ret = FreeLibrary(hmoduleRichEdit);
8918   ok(ret, "error: %d\n", (int) GetLastError());
8919 }
8920