1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22 
23 #define COBJMACROS
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <wingdi.h>
31 #include <winuser.h>
32 #include <winnls.h>
33 #include <ole2.h>
34 #include <richedit.h>
35 #include <richole.h>
36 #include <commdlg.h>
37 #include <time.h>
38 #include <wine/test.h>
39 
40 #define ID_RICHEDITTESTDBUTTON 0x123
41 
42 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
43 
44 #define ok_w3(format, szString1, szString2, szString3) \
45     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
46     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
47     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
48     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
49        format, string1, string2, string3);
50 
51 static HMODULE hmoduleRichEdit;
52 static BOOL is_lang_japanese;
53 
new_window(LPCSTR lpClassName,DWORD dwStyle,HWND parent)54 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
55   HWND hwnd;
56   hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
57                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
58                       hmoduleRichEdit, NULL);
59   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60   return hwnd;
61 }
62 
new_windowW(LPCWSTR lpClassName,DWORD dwStyle,HWND parent)63 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
64   HWND hwnd;
65   hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
66                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
67                       hmoduleRichEdit, NULL);
68   ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
69   return hwnd;
70 }
71 
new_richedit(HWND parent)72 static HWND new_richedit(HWND parent) {
73   return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
74 }
75 
new_richedit_with_style(HWND parent,DWORD style)76 static HWND new_richedit_with_style(HWND parent, DWORD style) {
77   return new_window(RICHEDIT_CLASS20A, style, parent);
78 }
79 
new_richeditW(HWND parent)80 static HWND new_richeditW(HWND parent) {
81   return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
82 }
83 
84 /* Keeps the window reponsive for the deley_time in seconds.
85  * This is useful for debugging a test to see what is happening. */
keep_responsive(time_t delay_time)86 static void keep_responsive(time_t delay_time)
87 {
88     MSG msg;
89     time_t end;
90 
91     /* The message pump uses PeekMessage() to empty the queue and then
92      * sleeps for 50ms before retrying the queue. */
93     end = time(NULL) + delay_time;
94     while (time(NULL) < end) {
95       if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
96         TranslateMessage(&msg);
97         DispatchMessageA(&msg);
98       } else {
99         Sleep(50);
100       }
101     }
102 }
103 
simulate_typing_characters(HWND hwnd,const char * szChars)104 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 {
106     int ret;
107 
108     while (*szChars != '\0') {
109         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
110         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
111         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
112         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
113         szChars++;
114     }
115 }
116 
hold_key(int vk)117 static BOOL hold_key(int vk)
118 {
119   BYTE key_state[256];
120   BOOL result;
121 
122   result = GetKeyboardState(key_state);
123   ok(result, "GetKeyboardState failed.\n");
124   if (!result) return FALSE;
125   key_state[vk] |= 0x80;
126   result = SetKeyboardState(key_state);
127   ok(result, "SetKeyboardState failed.\n");
128   return result != 0;
129 }
130 
release_key(int vk)131 static BOOL release_key(int vk)
132 {
133   BYTE key_state[256];
134   BOOL result;
135 
136   result = GetKeyboardState(key_state);
137   ok(result, "GetKeyboardState failed.\n");
138   if (!result) return FALSE;
139   key_state[vk] &= ~0x80;
140   result = SetKeyboardState(key_state);
141   ok(result, "SetKeyboardState failed.\n");
142   return result != 0;
143 }
144 
145 static const char haystack[] = "WINEWine wineWine wine WineWine";
146                              /* ^0        ^10       ^20       ^30 */
147 
148 struct find_s {
149   int start;
150   int end;
151   const char *needle;
152   int flags;
153   int expected_loc;
154 };
155 
156 
157 static struct find_s find_tests[] = {
158   /* Find in empty text */
159   {0, -1, "foo", FR_DOWN, -1},
160   {0, -1, "foo", 0, -1},
161   {0, -1, "", FR_DOWN, -1},
162   {20, 5, "foo", FR_DOWN, -1},
163   {5, 20, "foo", FR_DOWN, -1}
164 };
165 
166 static struct find_s find_tests2[] = {
167   /* No-result find */
168   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
169   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 
171   /* Subsequent finds */
172   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
173   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
174   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
175   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176 
177   /* Find backwards */
178   {19, 20, "Wine", FR_MATCHCASE, 13},
179   {10, 20, "Wine", FR_MATCHCASE, 4},
180   {20, 10, "Wine", FR_MATCHCASE, 13},
181 
182   /* Case-insensitive */
183   {1, 31, "wInE", FR_DOWN, 4},
184   {1, 31, "Wine", FR_DOWN, 4},
185 
186   /* High-to-low ranges */
187   {20, 5, "Wine", FR_DOWN, -1},
188   {2, 1, "Wine", FR_DOWN, -1},
189   {30, 29, "Wine", FR_DOWN, -1},
190   {20, 5, "Wine", 0, 13},
191 
192   /* Find nothing */
193   {5, 10, "", FR_DOWN, -1},
194   {10, 5, "", FR_DOWN, -1},
195   {0, -1, "", FR_DOWN, -1},
196   {10, 5, "", 0, -1},
197 
198   /* Whole-word search */
199   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
200   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
201   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
202   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
203   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
204   {11, -1, "winewine", FR_WHOLEWORD, 0},
205   {31, -1, "winewine", FR_WHOLEWORD, 23},
206 
207   /* Bad ranges */
208   {5, 200, "XXX", FR_DOWN, -1},
209   {-20, 20, "Wine", FR_DOWN, -1},
210   {-20, 20, "Wine", FR_DOWN, -1},
211   {-15, -20, "Wine", FR_DOWN, -1},
212   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 
214   /* Check the case noted in bug 4479 where matches at end aren't recognized */
215   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
216   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 
221   /* The backwards case of bug 4479; bounds look right
222    * Fails because backward find is wrong */
223   {19, 20, "WINE", FR_MATCHCASE, 0},
224   {0, 20, "WINE", FR_MATCHCASE, -1},
225 
226   {0, -1, "wineWine wine", 0, -1},
227 };
228 
atowstr(const char * str)229 static WCHAR *atowstr(const char *str)
230 {
231     WCHAR *ret;
232     DWORD len;
233     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
234     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
235     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
236     return ret;
237 }
238 
check_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * f,int id,BOOL unicode)239 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
240 {
241   int findloc;
242 
243   if(unicode){
244       FINDTEXTW ftw;
245       memset(&ftw, 0, sizeof(ftw));
246       ftw.chrg.cpMin = f->start;
247       ftw.chrg.cpMax = f->end;
248       ftw.lpstrText = atowstr(f->needle);
249 
250       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
251       ok(findloc == f->expected_loc,
252          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
253          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254 
255       findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
256       ok(findloc == f->expected_loc,
257          "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
258          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
259 
260       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
261   }else{
262       FINDTEXTA fta;
263       memset(&fta, 0, sizeof(fta));
264       fta.chrg.cpMin = f->start;
265       fta.chrg.cpMax = f->end;
266       fta.lpstrText = f->needle;
267 
268       findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
269       ok(findloc == f->expected_loc,
270          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
271          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
272   }
273 }
274 
check_EM_FINDTEXTEX(HWND hwnd,const char * name,struct find_s * f,int id,BOOL unicode)275 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
276     int id, BOOL unicode)
277 {
278   int findloc;
279   int expected_end_loc;
280 
281   if(unicode){
282       FINDTEXTEXW ftw;
283       memset(&ftw, 0, sizeof(ftw));
284       ftw.chrg.cpMin = f->start;
285       ftw.chrg.cpMax = f->end;
286       ftw.lpstrText = atowstr(f->needle);
287       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
288       ok(findloc == f->expected_loc,
289           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
290           name, id, f->needle, f->start, f->end, f->flags, findloc);
291       ok(ftw.chrgText.cpMin == f->expected_loc,
292           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
293           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
294       expected_end_loc = ((f->expected_loc == -1) ? -1
295             : f->expected_loc + strlen(f->needle));
296       ok(ftw.chrgText.cpMax == expected_end_loc,
297           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
298           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
299       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
300   }else{
301       FINDTEXTEXA fta;
302       memset(&fta, 0, sizeof(fta));
303       fta.chrg.cpMin = f->start;
304       fta.chrg.cpMax = f->end;
305       fta.lpstrText = f->needle;
306       findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
307       ok(findloc == f->expected_loc,
308           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
309           name, id, f->needle, f->start, f->end, f->flags, findloc);
310       ok(fta.chrgText.cpMin == f->expected_loc,
311           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
312           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
313       expected_end_loc = ((f->expected_loc == -1) ? -1
314             : f->expected_loc + strlen(f->needle));
315       ok(fta.chrgText.cpMax == expected_end_loc,
316           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
317           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
318   }
319 }
320 
run_tests_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * find,int num_tests,BOOL unicode)321 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
322     int num_tests, BOOL unicode)
323 {
324   int i;
325 
326   for (i = 0; i < num_tests; i++) {
327       check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
328       check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
329   }
330 }
331 
test_EM_FINDTEXT(BOOL unicode)332 static void test_EM_FINDTEXT(BOOL unicode)
333 {
334   HWND hwndRichEdit;
335   CHARFORMAT2A cf2;
336 
337   if(unicode)
338        hwndRichEdit = new_richeditW(NULL);
339   else
340        hwndRichEdit = new_richedit(NULL);
341 
342   /* Empty rich edit control */
343   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests), unicode);
344 
345   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
346 
347   /* Haystack text */
348   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2), unicode);
349 
350   /* Setting a format on an arbitrary range should have no effect in search
351      results. This tests correct offset reporting across runs. */
352   cf2.cbSize = sizeof(CHARFORMAT2A);
353   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
354   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
355   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
356   SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
357   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
358 
359   /* Haystack text, again */
360   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2, ARRAY_SIZE(find_tests2), 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, ARRAY_SIZE(find_tests2), unicode);
370 
371   DestroyWindow(hwndRichEdit);
372 }
373 
374 static const struct getline_s {
375   int line;
376   size_t buffer_len;
377   const char *text;
378 } gl[] = {
379   {0, 10, "foo bar\r"},
380   {1, 10, "\r"},
381   {2, 10, "bar\r"},
382   {3, 10, "\r"},
383 
384   /* Buffer smaller than line length */
385   {0, 2, "foo bar\r"},
386   {0, 1, "foo bar\r"},
387   {0, 0, "foo bar\r"}
388 };
389 
test_EM_GETLINE(void)390 static void test_EM_GETLINE(void)
391 {
392   int i;
393   HWND hwndRichEdit = new_richedit(NULL);
394   static const int nBuf = 1024;
395   char dest[1024], origdest[1024];
396   const char text[] = "foo bar\n"
397       "\n"
398       "bar\n";
399 
400   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
401 
402   memset(origdest, 0xBB, nBuf);
403   for (i = 0; i < ARRAY_SIZE(gl); i++)
404   {
405     int nCopied;
406     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
407     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
408     memset(dest, 0xBB, nBuf);
409     *(WORD *) dest = gl[i].buffer_len;
410 
411     /* EM_GETLINE appends a "\r\0" to the end of the line
412      * nCopied counts up to and including the '\r' */
413     nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
414     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
415        expected_nCopied);
416     /* two special cases since a parameter is passed via dest */
417     if (gl[i].buffer_len == 0)
418       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
419          "buffer_len=0\n");
420     else if (gl[i].buffer_len == 1)
421       ok(dest[0] == gl[i].text[0] && !dest[1] &&
422          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
423     else
424     {
425       /* Prepare hex strings of buffers to dump on failure. */
426       char expectedbuf[1024];
427       char resultbuf[1024];
428       int j;
429       resultbuf[0] = '\0';
430       for (j = 0; j < 32; j++)
431         sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
432       expectedbuf[0] = '\0';
433       for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
434         sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
435       for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
436         sprintf(expectedbuf+strlen(expectedbuf), "??");
437       for (; j < 32; j++) /* Bytes after declared buffer size */
438         sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
439 
440       /* Test the part of the buffer that is expected to be written according
441        * to the MSDN documentation fo EM_GETLINE, which does not state that
442        * a NULL terminating character will be added unless no text is copied.
443        *
444        * Windows NT does not append a NULL terminating character, but
445        * Windows 2000 and up do append a NULL terminating character if there
446        * is space in the buffer. The test will ignore this difference. */
447       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
448          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
449          i, expected_bytes_written, expectedbuf, resultbuf);
450       /* Test the part of the buffer after the declared length to make sure
451        * there are no buffer overruns. */
452       ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
453                   nBuf - gl[i].buffer_len),
454          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
455          i, expected_bytes_written, expectedbuf, resultbuf);
456     }
457   }
458 
459   DestroyWindow(hwndRichEdit);
460 }
461 
test_EM_LINELENGTH(void)462 static void test_EM_LINELENGTH(void)
463 {
464   HWND hwndRichEdit = new_richedit(NULL);
465   const char * text =
466         "richedit1\r"
467         "richedit1\n"
468         "richedit1\r\n"
469         "richedit1";
470   int offset_test[10][2] = {
471         {0, 9},
472         {5, 9},
473         {10, 9},
474         {15, 9},
475         {20, 9},
476         {25, 9},
477         {30, 9},
478         {35, 9},
479         {40, 0},
480         {45, 0},
481   };
482   int i;
483   LRESULT result;
484 
485   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
486 
487   for (i = 0; i < 10; i++) {
488     result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
489     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
490         offset_test[i][0], result, offset_test[i][1]);
491   }
492 
493   /* Test with multibyte character */
494   if (!is_lang_japanese)
495     skip("Skip multibyte character tests on non-Japanese platform\n");
496   else
497   {
498     const char *text1 =
499           "wine\n"
500           "richedit\x8e\xf0\n"
501           "wine";
502     int offset_test1[3][2] = {
503            {0, 4},  /* Line 1: |wine\n */
504            {5, 9},  /* Line 2: |richedit\x8e\xf0\n */
505            {15, 4}, /* Line 3: |wine */
506     };
507     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
508     for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
509       result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
510       ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
511          offset_test1[i][0], result, offset_test1[i][1]);
512     }
513   }
514 
515   DestroyWindow(hwndRichEdit);
516 }
517 
get_scroll_pos_y(HWND hwnd)518 static int get_scroll_pos_y(HWND hwnd)
519 {
520   POINT p = {-1, -1};
521   SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
522   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
523   return p.y;
524 }
525 
move_cursor(HWND hwnd,LONG charindex)526 static void move_cursor(HWND hwnd, LONG charindex)
527 {
528   CHARRANGE cr;
529   cr.cpMax = charindex;
530   cr.cpMin = charindex;
531   SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
532 }
533 
line_scroll(HWND hwnd,int amount)534 static void line_scroll(HWND hwnd, int amount)
535 {
536   SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
537 }
538 
test_EM_SCROLLCARET(void)539 static void test_EM_SCROLLCARET(void)
540 {
541   int prevY, curY;
542   const char text[] = "aa\n"
543       "this is a long line of text that should be longer than the "
544       "control's width\n"
545       "cc\n"
546       "dd\n"
547       "ee\n"
548       "ff\n"
549       "gg\n"
550       "hh\n";
551   /* The richedit window height needs to be large enough vertically to fit in
552    * more than two lines of text, so the new_richedit function can't be used
553    * since a height of 60 was not large enough on some systems.
554    */
555   HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
556                                    ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
557                                    0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
558   ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
559 
560   /* Can't verify this */
561   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
562 
563   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
564 
565   /* Caret above visible window */
566   line_scroll(hwndRichEdit, 3);
567   prevY = get_scroll_pos_y(hwndRichEdit);
568   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
569   curY = get_scroll_pos_y(hwndRichEdit);
570   ok(prevY != curY, "%d == %d\n", prevY, curY);
571 
572   /* Caret below visible window */
573   move_cursor(hwndRichEdit, sizeof(text) - 1);
574   line_scroll(hwndRichEdit, -3);
575   prevY = get_scroll_pos_y(hwndRichEdit);
576   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
577   curY = get_scroll_pos_y(hwndRichEdit);
578   ok(prevY != curY, "%d == %d\n", prevY, curY);
579 
580   /* Caret in visible window */
581   move_cursor(hwndRichEdit, sizeof(text) - 2);
582   prevY = get_scroll_pos_y(hwndRichEdit);
583   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
584   curY = get_scroll_pos_y(hwndRichEdit);
585   ok(prevY == curY, "%d != %d\n", prevY, curY);
586 
587   /* Caret still in visible window */
588   line_scroll(hwndRichEdit, -1);
589   prevY = get_scroll_pos_y(hwndRichEdit);
590   SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
591   curY = get_scroll_pos_y(hwndRichEdit);
592   ok(prevY == curY, "%d != %d\n", prevY, curY);
593 
594   DestroyWindow(hwndRichEdit);
595 }
596 
test_EM_POSFROMCHAR(void)597 static void test_EM_POSFROMCHAR(void)
598 {
599   HWND hwndRichEdit = new_richedit(NULL);
600   int i, expected;
601   LRESULT result;
602   unsigned int height = 0;
603   int xpos = 0;
604   POINTL pt;
605   LOCALESIGNATURE sig;
606   BOOL rtl;
607   PARAFORMAT2 fmt;
608   static const char text[] = "aa\n"
609       "this is a long line of text that should be longer than the "
610       "control's width\n"
611       "cc\n"
612       "dd\n"
613       "ee\n"
614       "ff\n"
615       "gg\n"
616       "hh\n";
617 
618   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
619                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
620          (sig.lsUsb[3] & 0x08000000) != 0);
621 
622   /* Fill the control to lines to ensure that most of them are offscreen */
623   for (i = 0; i < 50; i++)
624   {
625     /* Do not modify the string; it is exactly 16 characters long. */
626     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
627     SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
628   }
629 
630   /*
631    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
632    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
633    Richedit 3.0 accepts either of the above API conventions.
634    */
635 
636   /* Testing Richedit 2.0 API format */
637 
638   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
639      Since all lines are identical and drawn with the same font,
640      they should have the same height... right?
641    */
642   for (i = 0; i < 50; i++)
643   {
644     /* All the lines are 16 characters long */
645     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
646     if (i == 0)
647     {
648       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
649       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
650       xpos = LOWORD(result);
651     }
652     else if (i == 1)
653     {
654       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
655       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
656       height = HIWORD(result);
657     }
658     else
659     {
660       ros_skip_flaky
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 
test_EM_SETCHARFORMAT(void)750 static void test_EM_SETCHARFORMAT(void)
751 {
752   HWND hwndRichEdit = new_richedit(NULL);
753   CHARFORMAT2A cf2;
754   CHARFORMAT2W cfW;
755   CHARFORMATA cf1a;
756   CHARFORMATW cf1w;
757   int rc = 0;
758   int tested_effects[] = {
759     CFE_BOLD,
760     CFE_ITALIC,
761     CFE_UNDERLINE,
762     CFE_STRIKEOUT,
763     CFE_PROTECTED,
764     CFE_LINK,
765     CFE_SUBSCRIPT,
766     CFE_SUPERSCRIPT,
767     0
768   };
769   int i;
770   CHARRANGE cr;
771   LOCALESIGNATURE sig;
772   BOOL rtl;
773   DWORD expect_effects;
774 
775   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
776                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
777          (sig.lsUsb[3] & 0x08000000) != 0);
778 
779   /* check charformat defaults */
780   memset(&cf2, 0, sizeof(CHARFORMAT2A));
781   cf2.cbSize = sizeof(CHARFORMAT2A);
782   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
783   ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
784   expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
785   if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
786   ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
787   ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
788   ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
789   ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
790   ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
791   ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
792   ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
793   ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
794 
795   /* Invalid flags, CHARFORMAT2 structure blanked out */
796   memset(&cf2, 0, sizeof(cf2));
797   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
798   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
799 
800   /* A valid flag, CHARFORMAT2 structure blanked out */
801   memset(&cf2, 0, sizeof(cf2));
802   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
803   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
804 
805   /* A valid flag, CHARFORMAT2 structure blanked out */
806   memset(&cf2, 0, sizeof(cf2));
807   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
808   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
809 
810   /* A valid flag, CHARFORMAT2 structure blanked out */
811   memset(&cf2, 0, sizeof(cf2));
812   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
813   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
814 
815   /* A valid flag, CHARFORMAT2 structure blanked out */
816   memset(&cf2, 0, sizeof(cf2));
817   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
818   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
819 
820   /* Invalid flags, CHARFORMAT2 structure minimally filled */
821   memset(&cf2, 0, sizeof(cf2));
822   cf2.cbSize = sizeof(CHARFORMAT2A);
823   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
824   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
825   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
826   ok(rc == FALSE, "Should not be able to undo here.\n");
827   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
828 
829   /* A valid flag, CHARFORMAT2 structure minimally filled */
830   memset(&cf2, 0, sizeof(cf2));
831   cf2.cbSize = sizeof(CHARFORMAT2A);
832   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
833   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
835   ok(rc == FALSE, "Should not be able to undo here.\n");
836   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
837 
838   /* A valid flag, CHARFORMAT2 structure minimally filled */
839   memset(&cf2, 0, sizeof(cf2));
840   cf2.cbSize = sizeof(CHARFORMAT2A);
841   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
842   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
843   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
844   ok(rc == FALSE, "Should not be able to undo here.\n");
845   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
846 
847   /* A valid flag, CHARFORMAT2 structure minimally filled */
848   memset(&cf2, 0, sizeof(cf2));
849   cf2.cbSize = sizeof(CHARFORMAT2A);
850   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
851   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
852   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
853   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
854   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
855 
856   /* A valid flag, CHARFORMAT2 structure minimally filled */
857   memset(&cf2, 0, sizeof(cf2));
858   cf2.cbSize = sizeof(CHARFORMAT2A);
859   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
860   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
861   rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
862   ok(rc == TRUE, "Should not be able to undo here.\n");
863   SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
864 
865   cf2.cbSize = sizeof(CHARFORMAT2A);
866   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
867 
868   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
869   cf2.cbSize = sizeof(CHARFORMAT2A);
870   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
871   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
872   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
873 
874   /* wParam==0 is default char format, does not set modify */
875   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
876   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
877   ok(rc == 0, "Text marked as modified, expected not modified!\n");
878   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
879   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
880   if (! rtl)
881   {
882     rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
883     ok(rc == 0, "Text marked as modified, expected not modified!\n");
884   }
885   else
886     skip("RTL language found\n");
887 
888   /* wParam==SCF_SELECTION sets modify if nonempty selection */
889   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
890   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
891   ok(rc == 0, "Text marked as modified, expected not modified!\n");
892   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
893   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
894   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
895   ok(rc == 0, "Text marked as modified, expected not modified!\n");
896 
897   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
899   ok(rc == 0, "Text marked as modified, expected not modified!\n");
900   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
902   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
903   ok(rc == 0, "Text marked as modified, expected not modified!\n");
904   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
905   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
906   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
907   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
908   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
909 
910   /* wParam==SCF_ALL sets modify regardless of whether text is present */
911   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
912   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
913   ok(rc == 0, "Text marked as modified, expected not modified!\n");
914   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
915   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
916   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
917   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
918 
919   DestroyWindow(hwndRichEdit);
920 
921   /* EM_GETCHARFORMAT tests */
922   for (i = 0; tested_effects[i]; i++)
923   {
924     hwndRichEdit = new_richedit(NULL);
925     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
926 
927     /* Need to set a TrueType font to get consistent CFM_BOLD results */
928     memset(&cf2, 0, sizeof(CHARFORMAT2A));
929     cf2.cbSize = sizeof(CHARFORMAT2A);
930     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
931     cf2.dwEffects = 0;
932     strcpy(cf2.szFaceName, "Courier New");
933     cf2.wWeight = FW_DONTCARE;
934     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
935 
936     memset(&cf2, 0, sizeof(CHARFORMAT2A));
937     cf2.cbSize = sizeof(CHARFORMAT2A);
938     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
939     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
940     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
941           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
942           ||
943           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
944         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
945     ok((cf2.dwEffects & tested_effects[i]) == 0,
946         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
947 
948     memset(&cf2, 0, sizeof(CHARFORMAT2A));
949     cf2.cbSize = sizeof(CHARFORMAT2A);
950     cf2.dwMask = tested_effects[i];
951     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952       cf2.dwMask = CFM_SUPERSCRIPT;
953     cf2.dwEffects = tested_effects[i];
954     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
955     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
956 
957     memset(&cf2, 0, sizeof(CHARFORMAT2A));
958     cf2.cbSize = sizeof(CHARFORMAT2A);
959     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
963           ||
964           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
967         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
968 
969     memset(&cf2, 0, sizeof(CHARFORMAT2A));
970     cf2.cbSize = sizeof(CHARFORMAT2A);
971     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975           ||
976           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978     ok((cf2.dwEffects & tested_effects[i]) == 0,
979         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980 
981     memset(&cf2, 0, sizeof(CHARFORMAT2A));
982     cf2.cbSize = sizeof(CHARFORMAT2A);
983     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
987           ||
988           (cf2.dwMask & tested_effects[i]) == 0),
989         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990 
991     DestroyWindow(hwndRichEdit);
992   }
993 
994   for (i = 0; tested_effects[i]; i++)
995   {
996     hwndRichEdit = new_richedit(NULL);
997     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
998 
999     /* Need to set a TrueType font to get consistent CFM_BOLD results */
1000     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1001     cf2.cbSize = sizeof(CHARFORMAT2A);
1002     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1003     cf2.dwEffects = 0;
1004     strcpy(cf2.szFaceName, "Courier New");
1005     cf2.wWeight = FW_DONTCARE;
1006     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1007 
1008     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1009     cf2.cbSize = sizeof(CHARFORMAT2A);
1010     cf2.dwMask = tested_effects[i];
1011     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1012       cf2.dwMask = CFM_SUPERSCRIPT;
1013     cf2.dwEffects = tested_effects[i];
1014     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1015     SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016 
1017     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018     cf2.cbSize = sizeof(CHARFORMAT2A);
1019     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1020     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1023           ||
1024           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1025         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1026     ok((cf2.dwEffects & tested_effects[i]) == 0,
1027         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1028 
1029     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1030     cf2.cbSize = sizeof(CHARFORMAT2A);
1031     SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1032     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1033     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1034           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1035           ||
1036           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1037         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1038     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1039         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1040 
1041     memset(&cf2, 0, sizeof(CHARFORMAT2A));
1042     cf2.cbSize = sizeof(CHARFORMAT2A);
1043     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1044     SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1045     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1046           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1047           ||
1048           (cf2.dwMask & tested_effects[i]) == 0),
1049         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1050     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1051         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1052 
1053     DestroyWindow(hwndRichEdit);
1054   }
1055 
1056   /* Effects applied on an empty selection should take effect when selection is
1057      replaced with text */
1058   hwndRichEdit = new_richedit(NULL);
1059   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1061 
1062   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063   cf2.cbSize = sizeof(CHARFORMAT2A);
1064   cf2.dwMask = CFM_BOLD;
1065   cf2.dwEffects = CFE_BOLD;
1066   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067 
1068   /* Selection is now nonempty */
1069   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1070 
1071   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1072   cf2.cbSize = sizeof(CHARFORMAT2A);
1073   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1074   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1075 
1076   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1077       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1078   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1079       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1080 
1081 
1082   /* Set two effects on an empty selection */
1083   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1084   /* first clear bold, italic */
1085   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1086   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1087   cf2.cbSize = sizeof(CHARFORMAT2A);
1088   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1089   cf2.dwEffects = 0;
1090   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1091 
1092   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1093 
1094   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1095   cf2.cbSize = sizeof(CHARFORMAT2A);
1096   cf2.dwMask = CFM_BOLD;
1097   cf2.dwEffects = CFE_BOLD;
1098   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099   cf2.dwMask = CFM_ITALIC;
1100   cf2.dwEffects = CFE_ITALIC;
1101   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1102 
1103   /* Selection is now nonempty */
1104   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1105 
1106   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1107   cf2.cbSize = sizeof(CHARFORMAT2A);
1108   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1109   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1110 
1111   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1112       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1113   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1114       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1115 
1116   /* Setting the (empty) selection to exactly the same place as before should
1117      NOT clear the insertion style! */
1118   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1119   /* first clear bold, italic */
1120   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1121   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1122   cf2.cbSize = sizeof(CHARFORMAT2A);
1123   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1124   cf2.dwEffects = 0;
1125   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1126 
1127   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1128 
1129   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130   cf2.cbSize = sizeof(CHARFORMAT2A);
1131   cf2.dwMask = CFM_BOLD;
1132   cf2.dwEffects = CFE_BOLD;
1133   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1134 
1135   /* Empty selection in same place, insert style should NOT be forgotten here. */
1136   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1137 
1138   /* Selection is now nonempty */
1139   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1140 
1141   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1142   cf2.cbSize = sizeof(CHARFORMAT2A);
1143   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1144   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1145 
1146   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1147       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1148   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1149       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1150 
1151   /* Moving the selection will clear the insertion style */
1152   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1153   /* first clear bold, italic */
1154   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1155   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1156   cf2.cbSize = sizeof(CHARFORMAT2A);
1157   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1158   cf2.dwEffects = 0;
1159   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1160 
1161   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1162 
1163   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1164   cf2.cbSize = sizeof(CHARFORMAT2A);
1165   cf2.dwMask = CFM_BOLD;
1166   cf2.dwEffects = CFE_BOLD;
1167   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1168 
1169   /* Move selection and then put it back, insert style should be forgotten here. */
1170   SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1171   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1172 
1173   /* Selection is now nonempty */
1174   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1175 
1176   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1177   cf2.cbSize = sizeof(CHARFORMAT2A);
1178   SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1179   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1180 
1181   ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1182       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1183   ok((cf2.dwEffects & CFE_BOLD) == 0,
1184       "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1185 
1186   /* Ditto with EM_EXSETSEL */
1187   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1188   /* first clear bold, italic */
1189   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1190   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1191   cf2.cbSize = sizeof(CHARFORMAT2A);
1192   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1193   cf2.dwEffects = 0;
1194   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1195 
1196   cr.cpMin = 2; cr.cpMax = 2;
1197   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1198 
1199   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1200   cf2.cbSize = sizeof(CHARFORMAT2A);
1201   cf2.dwMask = CFM_BOLD;
1202   cf2.dwEffects = CFE_BOLD;
1203   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1204 
1205   /* Empty selection in same place, insert style should NOT be forgotten here. */
1206   cr.cpMin = 2; cr.cpMax = 2;
1207   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1208 
1209   /* Selection is now nonempty */
1210   SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1211 
1212   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1213   cf2.cbSize = sizeof(CHARFORMAT2A);
1214   cr.cpMin = 2; cr.cpMax = 6;
1215   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1216   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1217 
1218   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1219       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1220   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1221       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1222 
1223   /* show that wWeight is at the correct offset in CHARFORMAT2A */
1224   memset(&cf2, 0, sizeof(cf2));
1225   cf2.cbSize = sizeof(cf2);
1226   cf2.dwMask = CFM_WEIGHT;
1227   cf2.wWeight = 100;
1228   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1229   memset(&cf2, 0, sizeof(cf2));
1230   cf2.cbSize = sizeof(cf2);
1231   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1232   ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1233 
1234   memset(&cf2, 0, sizeof(cf2));
1235   cf2.cbSize = sizeof(cf2);
1236   cf2.dwMask = CFM_SPACING;
1237   cf2.sSpacing = 10;
1238   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1239   memset(&cf2, 0, sizeof(cf2));
1240   cf2.cbSize = sizeof(cf2);
1241   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1242   ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1243 
1244   /* show that wWeight is at the correct offset in CHARFORMAT2W */
1245   memset(&cfW, 0, sizeof(cfW));
1246   cfW.cbSize = sizeof(cfW);
1247   cfW.dwMask = CFM_WEIGHT;
1248   cfW.wWeight = 100;
1249   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1250   memset(&cfW, 0, sizeof(cfW));
1251   cfW.cbSize = sizeof(cfW);
1252   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1253   ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1254 
1255   memset(&cfW, 0, sizeof(cfW));
1256   cfW.cbSize = sizeof(cfW);
1257   cfW.dwMask = CFM_SPACING;
1258   cfW.sSpacing = 10;
1259   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1260   memset(&cfW, 0, sizeof(cfW));
1261   cfW.cbSize = sizeof(cfW);
1262   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1263   ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1264 
1265   /* test CFE_UNDERLINE and bUnderlineType interaction */
1266   /* clear bold, italic */
1267   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1268   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1269   cf2.cbSize = sizeof(CHARFORMAT2A);
1270   cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1271   cf2.dwEffects = 0;
1272   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273 
1274   /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1275   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1276   cf2.cbSize = sizeof(CHARFORMAT2A);
1277   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1278   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1279      "got %08x\n", cf2.dwMask);
1280   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1281   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1282 
1283   /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1284   cf2.dwMask = CFM_UNDERLINETYPE;
1285   cf2.bUnderlineType = CFU_UNDERLINE;
1286   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1287   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1288   cf2.cbSize = sizeof(CHARFORMAT2A);
1289   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1290   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1291      "got %08x\n", cf2.dwMask);
1292   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1293   ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1294 
1295   /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1296   cf2.dwMask = CFM_UNDERLINETYPE;
1297   cf2.bUnderlineType = CFU_UNDERLINENONE;
1298   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1299   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1300   cf2.cbSize = sizeof(CHARFORMAT2A);
1301   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1302   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1303      "got %08x\n", cf2.dwMask);
1304   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1305   ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1306 
1307   /* another underline type also sets CFE_UNDERLINE */
1308   cf2.dwMask = CFM_UNDERLINETYPE;
1309   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1310   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1311   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1312   cf2.cbSize = sizeof(CHARFORMAT2A);
1313   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1314   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1315      "got %08x\n", cf2.dwMask);
1316   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1317   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1318 
1319   /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1320   cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1321   cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1322   cf2.dwEffects = 0;
1323   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1324   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1325   cf2.cbSize = sizeof(CHARFORMAT2A);
1326   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1327   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1328      "got %08x\n", cf2.dwMask);
1329   ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1330   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1331 
1332   /* And turing it back on again by just setting CFE_UNDERLINE */
1333   cf2.dwMask = CFM_UNDERLINE;
1334   cf2.dwEffects = CFE_UNDERLINE;
1335   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1336   memset(&cf2, 0, sizeof(CHARFORMAT2A));
1337   cf2.cbSize = sizeof(CHARFORMAT2A);
1338   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1339   ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1340      "got %08x\n", cf2.dwMask);
1341   ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1342   ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1343 
1344   /* Check setting CFM_ALL2/CFM_EFFECTS2 in CHARFORMAT(A/W). */
1345   memset(&cf1a, 0, sizeof(CHARFORMATA));
1346   memset(&cf1w, 0, sizeof(CHARFORMATW));
1347   cf1a.cbSize = sizeof(CHARFORMATA);
1348   cf1w.cbSize = sizeof(CHARFORMATW);
1349   cf1a.dwMask = cf1w.dwMask = CFM_ALL2;
1350   cf1a.dwEffects = cf1w.dwEffects = CFM_EFFECTS2;
1351   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1352   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1353   /* flags only valid for CHARFORMAT2 should be masked out */
1354   ok((cf1a.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1355   ok((cf1a.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1356   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1357   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1358   ok((cf1w.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1359   ok((cf1w.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1360 
1361   DestroyWindow(hwndRichEdit);
1362 }
1363 
test_EM_SETTEXTMODE(void)1364 static void test_EM_SETTEXTMODE(void)
1365 {
1366   HWND hwndRichEdit = new_richedit(NULL);
1367   CHARFORMAT2A cf2, cf2test;
1368   CHARRANGE cr;
1369   int rc = 0;
1370 
1371   /*Attempt to use mutually exclusive modes*/
1372   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1373   ok(rc == E_INVALIDARG,
1374      "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1375 
1376   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1377   /*Insert text into the control*/
1378 
1379   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1380 
1381   /*Attempt to change the control to plain text mode*/
1382   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1383   ok(rc == E_UNEXPECTED,
1384      "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1385 
1386   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1387   If rich text is pasted, it should have the same formatting as the rest
1388   of the text in the control*/
1389 
1390   /*Italicize the text
1391   *NOTE: If the default text was already italicized, the test will simply
1392   reverse; in other words, it will copy a regular "wine" into a plain
1393   text window that uses an italicized format*/
1394   cf2.cbSize = sizeof(CHARFORMAT2A);
1395   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1396 
1397   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1398   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1399 
1400   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1401   ok(rc == 0, "Text marked as modified, expected not modified!\n");
1402 
1403   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1404   however, SCF_ALL has been implemented*/
1405   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1406   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1407 
1408   rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1409   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1410 
1411   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1412 
1413   /*Select the string "wine"*/
1414   cr.cpMin = 0;
1415   cr.cpMax = 4;
1416   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1417 
1418   /*Copy the italicized "wine" to the clipboard*/
1419   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1420 
1421   /*Reset the formatting to default*/
1422   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1423   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1424   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1425 
1426   /*Clear the text in the control*/
1427   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1428 
1429   /*Switch to Plain Text Mode*/
1430   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1431   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1432 
1433   /*Input "wine" again in normal format*/
1434   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1435 
1436   /*Paste the italicized "wine" into the control*/
1437   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1438 
1439   /*Select a character from the first "wine" string*/
1440   cr.cpMin = 2;
1441   cr.cpMax = 3;
1442   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1443 
1444   /*Retrieve its formatting*/
1445   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1446 
1447   /*Select a character from the second "wine" string*/
1448   cr.cpMin = 5;
1449   cr.cpMax = 6;
1450   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1451 
1452   /*Retrieve its formatting*/
1453   cf2test.cbSize = sizeof(CHARFORMAT2A);
1454   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1455 
1456   /*Compare the two formattings*/
1457     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1458       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1459        cf2.dwEffects, cf2test.dwEffects);
1460   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1461                          printing "wine" in the current format(normal)
1462                          pasting "wine" from the clipboard(italicized)
1463                          comparing the two formats(should differ)*/
1464 
1465   /*Attempt to switch with text in control*/
1466   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1467   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1468 
1469   /*Clear control*/
1470   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1471 
1472   /*Switch into Rich Text mode*/
1473   rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1474   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1475 
1476   /*Print "wine" in normal formatting into the control*/
1477   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1478 
1479   /*Paste italicized "wine" into the control*/
1480   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1481 
1482   /*Select text from the first "wine" string*/
1483   cr.cpMin = 1;
1484   cr.cpMax = 3;
1485   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1486 
1487   /*Retrieve its formatting*/
1488   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1489 
1490   /*Select text from the second "wine" string*/
1491   cr.cpMin = 6;
1492   cr.cpMax = 7;
1493   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1494 
1495   /*Retrieve its formatting*/
1496   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1497 
1498   /*Test that the two formattings are not the same*/
1499   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1500       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1501       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1502 
1503   DestroyWindow(hwndRichEdit);
1504 }
1505 
test_SETPARAFORMAT(void)1506 static void test_SETPARAFORMAT(void)
1507 {
1508   HWND hwndRichEdit = new_richedit(NULL);
1509   PARAFORMAT2 fmt;
1510   HRESULT ret;
1511   LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1512   fmt.cbSize = sizeof(PARAFORMAT2);
1513   fmt.dwMask = PFM_ALIGNMENT;
1514   fmt.wAlignment = PFA_LEFT;
1515 
1516   ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1517   ok(ret != 0, "expected non-zero got %d\n", ret);
1518 
1519   fmt.cbSize = sizeof(PARAFORMAT2);
1520   fmt.dwMask = -1;
1521   ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1522   /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1523    * between richedit different native builds of riched20.dll
1524    * used on different Windows versions. */
1525   ret &= ~PFM_TABLEROWDELIMITER;
1526   fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1527 
1528   ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1529   ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1530 
1531   /* Test some other paraformat field defaults */
1532   ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1533   ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1534   ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1535   ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1536 
1537   DestroyWindow(hwndRichEdit);
1538 }
1539 
test_TM_PLAINTEXT(void)1540 static void test_TM_PLAINTEXT(void)
1541 {
1542   /*Tests plain text properties*/
1543 
1544   HWND hwndRichEdit = new_richedit(NULL);
1545   CHARFORMAT2A cf2, cf2test;
1546   CHARRANGE cr;
1547   int rc = 0;
1548 
1549   /*Switch to plain text mode*/
1550 
1551   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1552   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1553 
1554   /*Fill control with text*/
1555 
1556   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1557 
1558   /*Select some text and bold it*/
1559 
1560   cr.cpMin = 10;
1561   cr.cpMax = 20;
1562   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1563   cf2.cbSize = sizeof(CHARFORMAT2A);
1564   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1565 
1566   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1567   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1568 
1569   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1570   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1571 
1572   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1573   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1574 
1575   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1576   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1577 
1578   /*Get the formatting of those characters*/
1579 
1580   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1581 
1582   /*Get the formatting of some other characters*/
1583   cf2test.cbSize = sizeof(CHARFORMAT2A);
1584   cr.cpMin = 21;
1585   cr.cpMax = 30;
1586   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1587   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1588 
1589   /*Test that they are the same as plain text allows only one formatting*/
1590 
1591   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1592      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1593      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1594 
1595   /*Fill the control with a "wine" string, which when inserted will be bold*/
1596 
1597   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1598 
1599   /*Copy the bolded "wine" string*/
1600 
1601   cr.cpMin = 0;
1602   cr.cpMax = 4;
1603   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1604   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1605 
1606   /*Swap back to rich text*/
1607 
1608   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1609   SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1610 
1611   /*Set the default formatting to bold italics*/
1612 
1613   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1614   cf2.dwMask |= CFM_ITALIC;
1615   cf2.dwEffects ^= CFE_ITALIC;
1616   rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1617   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1618 
1619   /*Set the text in the control to "wine", which will be bold and italicized*/
1620 
1621   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1622 
1623   /*Paste the plain text "wine" string, which should take the insert
1624    formatting, which at the moment is bold italics*/
1625 
1626   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1627 
1628   /*Select the first "wine" string and retrieve its formatting*/
1629 
1630   cr.cpMin = 1;
1631   cr.cpMax = 3;
1632   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1633   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1634 
1635   /*Select the second "wine" string and retrieve its formatting*/
1636 
1637   cr.cpMin = 5;
1638   cr.cpMax = 7;
1639   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1640   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1641 
1642   /*Compare the two formattings. They should be the same.*/
1643 
1644   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1645      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1646      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1647   DestroyWindow(hwndRichEdit);
1648 }
1649 
test_WM_GETTEXT(void)1650 static void test_WM_GETTEXT(void)
1651 {
1652     HWND hwndRichEdit = new_richedit(NULL);
1653     static const char text[] = "Hello. My name is RichEdit!";
1654     static const char text2[] = "Hello. My name is RichEdit!\r";
1655     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1656     char buffer[1024] = {0};
1657     int result;
1658 
1659     /* Baseline test with normal-sized buffer */
1660     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1661     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1662     ok(result == lstrlenA(buffer),
1663         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1664     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1665     result = strcmp(buffer,text);
1666     ok(result == 0,
1667         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1668 
1669     /* Test for returned value of WM_GETTEXTLENGTH */
1670     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1671     ok(result == lstrlenA(text),
1672         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1673         result, lstrlenA(text));
1674 
1675     /* Test for behavior in overflow case */
1676     memset(buffer, 0, 1024);
1677     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1678     ok(result == 0 ||
1679        result == lstrlenA(text) - 1, /* XP, win2k3 */
1680         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1681     result = strcmp(buffer,text);
1682     if (result)
1683         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1684     ok(result == 0,
1685         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1686 
1687     /* Baseline test with normal-sized buffer and carriage return */
1688     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1689     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1690     ok(result == lstrlenA(buffer),
1691         "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1692     result = strcmp(buffer,text2_after);
1693     ok(result == 0,
1694         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1695 
1696     /* Test for returned value of WM_GETTEXTLENGTH */
1697     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1698     ok(result == lstrlenA(text2_after),
1699         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1700         result, lstrlenA(text2_after));
1701 
1702     /* Test for behavior of CRLF conversion in case of overflow */
1703     memset(buffer, 0, 1024);
1704     result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1705     ok(result == 0 ||
1706        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1707         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1708     result = strcmp(buffer,text2);
1709     if (result)
1710         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1711     ok(result == 0,
1712         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1713 
1714     DestroyWindow(hwndRichEdit);
1715 }
1716 
test_EM_GETTEXTRANGE(void)1717 static void test_EM_GETTEXTRANGE(void)
1718 {
1719     HWND hwndRichEdit = new_richedit(NULL);
1720     const char * text1 = "foo bar\r\nfoo bar";
1721     const char * text2 = "foo bar\rfoo bar";
1722     const char * expect = "bar\rfoo";
1723     char buffer[1024] = {0};
1724     LRESULT result;
1725     TEXTRANGEA textRange;
1726 
1727     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1728 
1729     textRange.lpstrText = buffer;
1730     textRange.chrg.cpMin = 4;
1731     textRange.chrg.cpMax = 11;
1732     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1733     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1734     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1735 
1736     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1737 
1738     textRange.lpstrText = buffer;
1739     textRange.chrg.cpMin = 4;
1740     textRange.chrg.cpMax = 11;
1741     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1742     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1743     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1744 
1745     /* cpMax of text length is used instead of -1 in this case */
1746     textRange.lpstrText = buffer;
1747     textRange.chrg.cpMin = 0;
1748     textRange.chrg.cpMax = -1;
1749     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1750     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1751     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1752 
1753     /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1754     textRange.lpstrText = buffer;
1755     textRange.chrg.cpMin = -1;
1756     textRange.chrg.cpMax = 1;
1757     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1758     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1759     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1760 
1761     /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1762     textRange.lpstrText = buffer;
1763     textRange.chrg.cpMin = 1;
1764     textRange.chrg.cpMax = -1;
1765     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1766     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1767     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1768 
1769     /* no end character is copied if cpMax - cpMin < 0 */
1770     textRange.lpstrText = buffer;
1771     textRange.chrg.cpMin = 5;
1772     textRange.chrg.cpMax = 5;
1773     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1774     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1775     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1776 
1777     /* cpMax of text length is used if cpMax > text length*/
1778     textRange.lpstrText = buffer;
1779     textRange.chrg.cpMin = 0;
1780     textRange.chrg.cpMax = 1000;
1781     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1782     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1783     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1784 
1785     /* Test with multibyte character */
1786     if (!is_lang_japanese)
1787         skip("Skip multibyte character tests on non-Japanese platform\n");
1788     else
1789     {
1790         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1791         textRange.chrg.cpMin = 4;
1792         textRange.chrg.cpMax = 8;
1793         result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1794         todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1795         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1796     }
1797 
1798     DestroyWindow(hwndRichEdit);
1799 }
1800 
test_EM_GETSELTEXT(void)1801 static void test_EM_GETSELTEXT(void)
1802 {
1803     HWND hwndRichEdit = new_richedit(NULL);
1804     const char * text1 = "foo bar\r\nfoo bar";
1805     const char * text2 = "foo bar\rfoo bar";
1806     const char * expect = "bar\rfoo";
1807     char buffer[1024] = {0};
1808     LRESULT result;
1809 
1810     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1811 
1812     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1813     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1814     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1815     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1816 
1817     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1818 
1819     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1820     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1821     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1822     ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1823 
1824     /* Test with multibyte character */
1825     if (!is_lang_japanese)
1826         skip("Skip multibyte character tests on non-Japanese platform\n");
1827     else
1828     {
1829         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1830         SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1831         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1832         todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1833         todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1834     }
1835 
1836     DestroyWindow(hwndRichEdit);
1837 }
1838 
1839 /* FIXME: need to test unimplemented options and robustly test wparam */
test_EM_SETOPTIONS(void)1840 static void test_EM_SETOPTIONS(void)
1841 {
1842     HWND hwndRichEdit;
1843     static const char text[] = "Hello. My name is RichEdit!";
1844     char buffer[1024] = {0};
1845     DWORD dwStyle, options, oldOptions;
1846     DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1847                          ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1848                          ES_SELECTIONBAR|ES_VERTICAL;
1849 
1850     /* Test initial options. */
1851     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1852                                 0, 0, 200, 60, NULL, NULL,
1853                                 hmoduleRichEdit, NULL);
1854     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1855        RICHEDIT_CLASS20A, (int) GetLastError());
1856     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1857     ok(options == 0, "Incorrect initial options %x\n", options);
1858     DestroyWindow(hwndRichEdit);
1859 
1860     hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1861                                 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1862                                 0, 0, 200, 60, NULL, NULL,
1863                                 hmoduleRichEdit, NULL);
1864     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1865        RICHEDIT_CLASS20A, (int) GetLastError());
1866     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1867     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1868     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1869        "Incorrect initial options %x\n", options);
1870 
1871     /* NEGATIVE TESTING - NO OPTIONS SET */
1872     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1873     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1874 
1875     /* testing no readonly by sending 'a' to the control*/
1876     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1877     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1878     ok(buffer[0]=='a',
1879        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1880     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1881 
1882     /* READONLY - sending 'a' to the control */
1883     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1884     SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1885     SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1886     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1887     ok(buffer[0]==text[0],
1888        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1889 
1890     /* EM_SETOPTIONS changes the window style, but changing the
1891      * window style does not change the options. */
1892     dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1893     ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1894     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1895     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1896     ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1897     /* Confirm that the text is still read only. */
1898     SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1899     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1900     ok(buffer[0]==text[0],
1901        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1902 
1903     oldOptions = options;
1904     SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1905     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1906     ok(options == oldOptions,
1907        "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1908 
1909     DestroyWindow(hwndRichEdit);
1910 }
1911 
check_CFE_LINK_selection(HWND hwnd,int sel_start,int sel_end)1912 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1913 {
1914   CHARFORMAT2A text_format;
1915   text_format.cbSize = sizeof(text_format);
1916   SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1917   SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1918   return (text_format.dwEffects & CFE_LINK) != 0;
1919 }
1920 
check_CFE_LINK_rcvd(HWND hwnd,BOOL is_url,const char * url)1921 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1922 {
1923   BOOL link_present = FALSE;
1924 
1925   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1926   if (is_url)
1927   { /* control text is url; should get CFE_LINK */
1928     ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1929   }
1930   else
1931   {
1932     ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1933   }
1934 }
1935 
new_static_wnd(HWND parent)1936 static HWND new_static_wnd(HWND parent) {
1937   return new_window("Static", 0, parent);
1938 }
1939 
test_EM_AUTOURLDETECT(void)1940 static void test_EM_AUTOURLDETECT(void)
1941 {
1942   /* DO NOT change the properties of the first two elements. To shorten the
1943      tests, all tests after WM_SETTEXT test just the first two elements -
1944      one non-URL and one URL */
1945   struct urls_s {
1946     const char *text;
1947     BOOL is_url;
1948   } urls[12] = {
1949     {"winehq.org", FALSE},
1950     {"http://www.winehq.org", TRUE},
1951     {"http//winehq.org", FALSE},
1952     {"ww.winehq.org", FALSE},
1953     {"www.winehq.org", TRUE},
1954     {"ftp://192.168.1.1", TRUE},
1955     {"ftp//192.168.1.1", FALSE},
1956     {"mailto:your@email.com", TRUE},
1957     {"prospero:prosperoserver", TRUE},
1958     {"telnet:test", TRUE},
1959     {"news:newserver", TRUE},
1960     {"wais:waisserver", TRUE}
1961   };
1962 
1963   int i, j;
1964   int urlRet=-1;
1965   HWND hwndRichEdit, parent;
1966 
1967   /* All of the following should cause the URL to be detected  */
1968   const char * templates_delim[] = {
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\r on it",
1972     "This is some text with ---X--- on it",
1973     "This is some text with \"X\" on it",
1974     "This is some text with 'X' on it",
1975     "This is some text with 'X' on it",
1976     "This is some text with :X: on it",
1977 
1978     "This text ends with X",
1979 
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 
1986     "This is some text with (X on it",
1987     "This is some text with \rX 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   };
1993   /* None of these should cause the URL to be detected */
1994   const char * templates_non_delim[] = {
1995     "This is some text with |X| on it",
1996     "This is some text with *X* on it",
1997     "This is some text with /X/ on it",
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     "This is some text with %X on it",
2008     "This is some text with #X on it",
2009     "This is some text with @X on it",
2010     "This is some text with \\X on it",
2011     "This is some text with _X on it",
2012   };
2013   /* All of these cause the URL detection to be extended by one more byte,
2014      thus demonstrating that the tested character is considered as part
2015      of the URL. */
2016   const char * templates_xten_delim[] = {
2017     "This is some text with X| on it",
2018     "This is some text with X* on it",
2019     "This is some text with X/ on it",
2020     "This is some text with X+ on it",
2021     "This is some text with X% on it",
2022     "This is some text with X# on it",
2023     "This is some text with X@ on it",
2024     "This is some text with X\\ on it",
2025     "This is some text with X_ on it",
2026   };
2027   /* These delims act as neutral breaks.  Whether the url is ended
2028      or not depends on the next non-neutral character.  We'll test
2029      with Y unchanged, in which case the url should include the
2030      deliminator and the Y.  We'll also test with the Y changed
2031      to a space, in which case the url stops before the
2032      deliminator. */
2033   const char * templates_neutral_delim[] = {
2034     "This is some text with X-Y on it",
2035     "This is some text with X--Y on it",
2036     "This is some text with X!Y on it",
2037     "This is some text with X[Y on it",
2038     "This is some text with X]Y on it",
2039     "This is some text with X{Y on it",
2040     "This is some text with X}Y on it",
2041     "This is some text with X(Y on it",
2042     "This is some text with X)Y on it",
2043     "This is some text with X\"Y on it",
2044     "This is some text with X;Y on it",
2045     "This is some text with X:Y on it",
2046     "This is some text with X'Y on it",
2047     "This is some text with X?Y on it",
2048     "This is some text with X<Y on it",
2049     "This is some text with X>Y on it",
2050     "This is some text with X.Y on it",
2051     "This is some text with X,Y on it",
2052   };
2053   char buffer[1024];
2054 
2055   parent = new_static_wnd(NULL);
2056   hwndRichEdit = new_richedit(parent);
2057   /* Try and pass EM_AUTOURLDETECT some test wParam values */
2058   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2059   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2060   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2061   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2062   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2063   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2064   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2065   urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2066   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2067   /* for each url, check the text to see if CFE_LINK effect is present */
2068   for (i = 0; i < ARRAY_SIZE(urls); i++) {
2069 
2070     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2071     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2072     check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2073 
2074     /* Link detection should happen immediately upon WM_SETTEXT */
2075     SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2076     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2077     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2078   }
2079   DestroyWindow(hwndRichEdit);
2080 
2081   /* Test detection of URLs within normal text - WM_SETTEXT case. */
2082   for (i = 0; i < ARRAY_SIZE(urls); i++) {
2083     hwndRichEdit = new_richedit(parent);
2084 
2085     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2086       char * at_pos;
2087       int at_offset;
2088       int end_offset;
2089 
2090       at_pos = strchr(templates_delim[j], 'X');
2091       at_offset = at_pos - templates_delim[j];
2092       memcpy(buffer, templates_delim[j], at_offset);
2093       buffer[at_offset] = '\0';
2094       strcat(buffer, urls[i].text);
2095       strcat(buffer, templates_delim[j] + at_offset + 1);
2096       end_offset = at_offset + strlen(urls[i].text);
2097 
2098       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2099       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2100 
2101       /* This assumes no templates start with the URL itself, and that they
2102          have at least two characters before the URL text */
2103       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2104         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2105       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2106         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2107       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2108         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2109 
2110       if (urls[i].is_url)
2111       {
2112         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2114         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2116       }
2117       else
2118       {
2119         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2120           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2121         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2122           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2123       }
2124       if (buffer[end_offset] != '\0')
2125       {
2126         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2127           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2128         if (buffer[end_offset +1] != '\0')
2129         {
2130           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2131             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2132         }
2133       }
2134     }
2135 
2136     for (j = 0; j < ARRAY_SIZE(templates_non_delim); j++) {
2137       char * at_pos;
2138       int at_offset;
2139       int end_offset;
2140 
2141       at_pos = strchr(templates_non_delim[j], 'X');
2142       at_offset = at_pos - templates_non_delim[j];
2143       memcpy(buffer, templates_non_delim[j], at_offset);
2144       buffer[at_offset] = '\0';
2145       strcat(buffer, urls[i].text);
2146       strcat(buffer, templates_non_delim[j] + at_offset + 1);
2147       end_offset = at_offset + strlen(urls[i].text);
2148 
2149       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2150       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2151 
2152       /* This assumes no templates start with the URL itself, and that they
2153          have at least two characters before the URL text */
2154       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2155         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2156       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2157         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2158       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2159         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2160 
2161       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2163       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165       if (buffer[end_offset] != '\0')
2166       {
2167         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2168           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2169         if (buffer[end_offset +1] != '\0')
2170         {
2171           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2172             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2173         }
2174       }
2175     }
2176 
2177     for (j = 0; j < ARRAY_SIZE(templates_xten_delim); j++) {
2178       char * at_pos;
2179       int at_offset;
2180       int end_offset;
2181 
2182       at_pos = strchr(templates_xten_delim[j], 'X');
2183       at_offset = at_pos - templates_xten_delim[j];
2184       memcpy(buffer, templates_xten_delim[j], at_offset);
2185       buffer[at_offset] = '\0';
2186       strcat(buffer, urls[i].text);
2187       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2188       end_offset = at_offset + strlen(urls[i].text);
2189 
2190       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2191       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2192 
2193       /* This assumes no templates start with the URL itself, and that they
2194          have at least two characters before the URL text */
2195       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2196         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2197       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2198         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2199       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2200         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2201 
2202       if (urls[i].is_url)
2203       {
2204         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2206         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2208         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2209           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2210       }
2211       else
2212       {
2213         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2214           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2215         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2216           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2217         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2218           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2219       }
2220       if (buffer[end_offset +1] != '\0')
2221       {
2222         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2223           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2224         if (buffer[end_offset +2] != '\0')
2225         {
2226           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2227             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2228         }
2229       }
2230     }
2231 
2232     for (j = 0; j < ARRAY_SIZE(templates_neutral_delim); j++) {
2233       char * at_pos, * end_pos;
2234       int at_offset;
2235       int end_offset;
2236 
2237       if (!urls[i].is_url) continue;
2238 
2239       at_pos = strchr(templates_neutral_delim[j], 'X');
2240       at_offset = at_pos - templates_neutral_delim[j];
2241       memcpy(buffer, templates_neutral_delim[j], at_offset);
2242       buffer[at_offset] = '\0';
2243       strcat(buffer, urls[i].text);
2244       strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2245 
2246       end_pos = strchr(buffer, 'Y');
2247       end_offset = end_pos - buffer;
2248 
2249       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2250       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2251 
2252       /* This assumes no templates start with the URL itself, and that they
2253          have at least two characters before the URL text */
2254       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2255          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2256       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2257          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2258       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2259          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2260 
2261       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2262          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2263       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2264          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2265       ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2266          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2267 
2268       *end_pos = ' ';
2269 
2270       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2271       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2272 
2273       ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2274          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2275       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2276          "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2277       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2278          "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2279     }
2280 
2281     DestroyWindow(hwndRichEdit);
2282     hwndRichEdit = NULL;
2283   }
2284 
2285   /* Test detection of URLs within normal text - WM_CHAR case. */
2286   /* Test only the first two URL examples for brevity */
2287   for (i = 0; i < 2; i++) {
2288     hwndRichEdit = new_richedit(parent);
2289 
2290     /* Also for brevity, test only the first three delimiters */
2291     for (j = 0; j < 3; j++) {
2292       char * at_pos;
2293       int at_offset;
2294       int end_offset;
2295       int u, v;
2296 
2297       at_pos = strchr(templates_delim[j], 'X');
2298       at_offset = at_pos - templates_delim[j];
2299       end_offset = at_offset + strlen(urls[i].text);
2300 
2301       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2302       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2303       for (u = 0; templates_delim[j][u]; u++) {
2304         if (templates_delim[j][u] == '\r') {
2305           simulate_typing_characters(hwndRichEdit, "\r");
2306         } else if (templates_delim[j][u] != 'X') {
2307           SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2308         } else {
2309           for (v = 0; urls[i].text[v]; v++) {
2310             SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2311           }
2312         }
2313       }
2314       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2315 
2316       /* This assumes no templates start with the URL itself, and that they
2317          have at least two characters before the URL text */
2318       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2319         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2320       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2321         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2322       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2323         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2324 
2325       if (urls[i].is_url)
2326       {
2327         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2328           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2329         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2330           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2331       }
2332       else
2333       {
2334         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2336         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2338       }
2339       if (buffer[end_offset] != '\0')
2340       {
2341         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2342           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2343         if (buffer[end_offset +1] != '\0')
2344         {
2345           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2346             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2347         }
2348       }
2349 
2350       /* The following will insert a paragraph break after the first character
2351          of the URL candidate, thus breaking the URL. It is expected that the
2352          CFE_LINK attribute should break across both pieces of the URL */
2353       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2354       simulate_typing_characters(hwndRichEdit, "\r");
2355       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2356 
2357       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2358         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2359       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2360         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2361       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2362         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2363 
2364       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2365         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2366       /* end_offset moved because of paragraph break */
2367       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2368         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2369       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2370       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
2371       {
2372         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2373           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2374         if (buffer[end_offset +2] != '\0')
2375         {
2376           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2377             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2378         }
2379       }
2380 
2381       /* The following will remove the just-inserted paragraph break, thus
2382          restoring the URL */
2383       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2384       simulate_typing_characters(hwndRichEdit, "\b");
2385       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2386 
2387       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2388         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2389       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2390         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2391       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2392         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2393 
2394       if (urls[i].is_url)
2395       {
2396         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2397           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2398         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2399           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2400       }
2401       else
2402       {
2403         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2404           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2405         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2406           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2407       }
2408       if (buffer[end_offset] != '\0')
2409       {
2410         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2411           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2412         if (buffer[end_offset +1] != '\0')
2413         {
2414           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2415             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2416         }
2417       }
2418     }
2419     DestroyWindow(hwndRichEdit);
2420     hwndRichEdit = NULL;
2421   }
2422 
2423   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2424   /* Test just the first two URL examples for brevity */
2425   for (i = 0; i < 2; i++) {
2426     SETTEXTEX st;
2427 
2428     hwndRichEdit = new_richedit(parent);
2429 
2430     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2431        be detected:
2432        1) Set entire text, a la WM_SETTEXT
2433        2) Set a selection of the text to the URL
2434        3) Set a portion of the text at a time, which eventually results in
2435           an URL
2436        All of them should give equivalent results
2437      */
2438 
2439     /* Set entire text in one go, like WM_SETTEXT */
2440     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2441       char * at_pos;
2442       int at_offset;
2443       int end_offset;
2444 
2445       st.codepage = CP_ACP;
2446       st.flags = ST_DEFAULT;
2447 
2448       at_pos = strchr(templates_delim[j], 'X');
2449       at_offset = at_pos - templates_delim[j];
2450       memcpy(buffer, templates_delim[j], at_offset);
2451       buffer[at_offset] = '\0';
2452       strcat(buffer, urls[i].text);
2453       strcat(buffer, templates_delim[j] + at_offset + 1);
2454       end_offset = at_offset + strlen(urls[i].text);
2455 
2456       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2457       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2458 
2459       /* This assumes no templates start with the URL itself, and that they
2460          have at least two characters before the URL text */
2461       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2462         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2463       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2464         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2465       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2466         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2467 
2468       if (urls[i].is_url)
2469       {
2470         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2471           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2472         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2473           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2474       }
2475       else
2476       {
2477         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2478           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2479         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2480           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2481       }
2482       if (buffer[end_offset] != '\0')
2483       {
2484         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2485           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2486         if (buffer[end_offset +1] != '\0')
2487         {
2488           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2489             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2490         }
2491       }
2492     }
2493 
2494     /* Set selection with X to the URL */
2495     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2496       char * at_pos;
2497       int at_offset;
2498       int end_offset;
2499 
2500       at_pos = strchr(templates_delim[j], 'X');
2501       at_offset = at_pos - templates_delim[j];
2502       end_offset = at_offset + strlen(urls[i].text);
2503 
2504       st.codepage = CP_ACP;
2505       st.flags = ST_DEFAULT;
2506       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2507       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2508       st.flags = ST_SELECTION;
2509       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2510       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2511       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2512 
2513       /* This assumes no templates start with the URL itself, and that they
2514          have at least two characters before the URL text */
2515       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2516         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2517       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2518         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2519       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2520         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2521 
2522       if (urls[i].is_url)
2523       {
2524         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2525           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2526         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2527           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2528       }
2529       else
2530       {
2531         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2532           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2533         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2534           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2535       }
2536       if (buffer[end_offset] != '\0')
2537       {
2538         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2539           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2540         if (buffer[end_offset +1] != '\0')
2541         {
2542           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2543             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2544         }
2545       }
2546     }
2547 
2548     /* Set selection with X to the first character of the URL, then the rest */
2549     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2550       char * at_pos;
2551       int at_offset;
2552       int end_offset;
2553 
2554       at_pos = strchr(templates_delim[j], 'X');
2555       at_offset = at_pos - templates_delim[j];
2556       end_offset = at_offset + strlen(urls[i].text);
2557 
2558       strcpy(buffer, "YY");
2559       buffer[0] = urls[i].text[0];
2560 
2561       st.codepage = CP_ACP;
2562       st.flags = ST_DEFAULT;
2563       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2564       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2565       st.flags = ST_SELECTION;
2566       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2567       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2568       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2569       SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2570       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2571 
2572       /* This assumes no templates start with the URL itself, and that they
2573          have at least two characters before the URL text */
2574       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2575         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2576       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2577         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2578       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2579         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2580 
2581       if (urls[i].is_url)
2582       {
2583         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2584           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2585         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2586           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2587       }
2588       else
2589       {
2590         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2591           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2592         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2593           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2594       }
2595       if (buffer[end_offset] != '\0')
2596       {
2597         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2598           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2599         if (buffer[end_offset +1] != '\0')
2600         {
2601           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2602             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2603         }
2604       }
2605     }
2606 
2607     DestroyWindow(hwndRichEdit);
2608     hwndRichEdit = NULL;
2609   }
2610 
2611   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2612   /* Test just the first two URL examples for brevity */
2613   for (i = 0; i < 2; i++) {
2614     hwndRichEdit = new_richedit(parent);
2615 
2616     /* Set selection with X to the URL */
2617     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2618       char * at_pos;
2619       int at_offset;
2620       int end_offset;
2621 
2622       at_pos = strchr(templates_delim[j], 'X');
2623       at_offset = at_pos - templates_delim[j];
2624       end_offset = at_offset + strlen(urls[i].text);
2625 
2626       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2627       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2628       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2629       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2630       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2631 
2632       /* This assumes no templates start with the URL itself, and that they
2633          have at least two characters before the URL text */
2634       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2635         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2636       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2637         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2638       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2639         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2640 
2641       if (urls[i].is_url)
2642       {
2643         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2644           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2645         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2646           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2647       }
2648       else
2649       {
2650         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2651           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2652         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2653           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2654       }
2655       if (buffer[end_offset] != '\0')
2656       {
2657         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2658           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2659         if (buffer[end_offset +1] != '\0')
2660         {
2661           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2662             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2663         }
2664       }
2665     }
2666 
2667     /* Set selection with X to the first character of the URL, then the rest */
2668     for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2669       char * at_pos;
2670       int at_offset;
2671       int end_offset;
2672 
2673       at_pos = strchr(templates_delim[j], 'X');
2674       at_offset = at_pos - templates_delim[j];
2675       end_offset = at_offset + strlen(urls[i].text);
2676 
2677       strcpy(buffer, "YY");
2678       buffer[0] = urls[i].text[0];
2679 
2680       SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2681       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2682       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2683       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2684       SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2685       SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2686       SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2687 
2688       /* This assumes no templates start with the URL itself, and that they
2689          have at least two characters before the URL text */
2690       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2691         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2692       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2693         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2694       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2695         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2696 
2697       if (urls[i].is_url)
2698       {
2699         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2700           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2701         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2702           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2703       }
2704       else
2705       {
2706         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2707           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2708         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2709           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2710       }
2711       if (buffer[end_offset] != '\0')
2712       {
2713         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2714           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2715         if (buffer[end_offset +1] != '\0')
2716         {
2717           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2718             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2719         }
2720       }
2721     }
2722 
2723     DestroyWindow(hwndRichEdit);
2724     hwndRichEdit = NULL;
2725   }
2726 
2727   DestroyWindow(parent);
2728 }
2729 
test_EM_SCROLL(void)2730 static void test_EM_SCROLL(void)
2731 {
2732   int i, j;
2733   int r; /* return value */
2734   int expr; /* expected return value */
2735   HWND hwndRichEdit = new_richedit(NULL);
2736   int y_before, y_after; /* units of lines of text */
2737 
2738   /* test a richedit box containing a single line of text */
2739   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2740   expr = 0x00010000;
2741   for (i = 0; i < 4; i++) {
2742     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2743 
2744     r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2745     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2746     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2747        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2748     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2749        "(i == %d)\n", y_after, i);
2750   }
2751 
2752   /*
2753    * test a richedit box that will scroll. There are two general
2754    * cases: the case without any long lines and the case with a long
2755    * line.
2756    */
2757   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2758     if (i == 0)
2759       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2760     else
2761       SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2762                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2763                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2764                   "LONG LINE \nb\nc\nd\ne");
2765     for (j = 0; j < 12; j++) /* reset scroll position to top */
2766       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2767 
2768     /* get first visible line */
2769     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2770     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2771 
2772     /* get new current first visible line */
2773     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2774 
2775     ok(((r & 0xffffff00) == 0x00010000) &&
2776        ((r & 0x000000ff) != 0x00000000),
2777        "EM_SCROLL page down didn't scroll by a small positive number of "
2778        "lines (r == 0x%08x)\n", r);
2779     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2780        "(line %d scrolled to line %d\n", y_before, y_after);
2781 
2782     y_before = y_after;
2783 
2784     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2785     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2786     ok(((r & 0xffffff00) == 0x0001ff00),
2787        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2788        "(r == 0x%08x)\n", r);
2789     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2790        "%d scrolled to line %d\n", y_before, y_after);
2791 
2792     y_before = y_after;
2793 
2794     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2795 
2796     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2797 
2798     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2799        "(r == 0x%08x)\n", r);
2800     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2801        "1 line (%d scrolled to %d)\n", y_before, y_after);
2802 
2803     y_before = y_after;
2804 
2805     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2806 
2807     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2808 
2809     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2810        "(r == 0x%08x)\n", r);
2811     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2812        "line (%d scrolled to %d)\n", y_before, y_after);
2813 
2814     y_before = y_after;
2815 
2816     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2817                     SB_LINEUP, 0); /* lineup beyond top */
2818 
2819     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2820 
2821     ok(r == 0x00010000,
2822        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2823     ok(y_before == y_after,
2824        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2825 
2826     y_before = y_after;
2827 
2828     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2829                     SB_PAGEUP, 0);/*page up beyond top */
2830 
2831     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2832 
2833     ok(r == 0x00010000,
2834        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2835     ok(y_before == y_after,
2836        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2837 
2838     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2839       SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2840     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2841     r = SendMessageA(hwndRichEdit, EM_SCROLL,
2842                     SB_PAGEDOWN, 0); /* page down beyond bot */
2843     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2844 
2845     ok(r == 0x00010000,
2846        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2847     ok(y_before == y_after,
2848        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2849        y_before, y_after);
2850 
2851     y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2852     r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2853     y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2854 
2855     ok(r == 0x00010000,
2856        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2857     ok(y_before == y_after,
2858        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2859        y_before, y_after);
2860   }
2861   DestroyWindow(hwndRichEdit);
2862 }
2863 
2864 static unsigned int recursionLevel = 0;
2865 static unsigned int WM_SIZE_recursionLevel = 0;
2866 static BOOL bailedOutOfRecursion = FALSE;
2867 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2868 
RicheditStupidOverrideProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)2869 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2870 {
2871     LRESULT r;
2872 
2873     if (bailedOutOfRecursion) return 0;
2874     if (recursionLevel >= 32) {
2875         bailedOutOfRecursion = TRUE;
2876         return 0;
2877     }
2878 
2879     recursionLevel++;
2880     switch (message) {
2881     case WM_SIZE:
2882         WM_SIZE_recursionLevel++;
2883         r = richeditProc(hwnd, message, wParam, lParam);
2884         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2885         ShowScrollBar(hwnd, SB_VERT, TRUE);
2886         WM_SIZE_recursionLevel--;
2887         break;
2888     default:
2889         r = richeditProc(hwnd, message, wParam, lParam);
2890         break;
2891     }
2892     recursionLevel--;
2893     return r;
2894 }
2895 
test_scrollbar_visibility(void)2896 static void test_scrollbar_visibility(void)
2897 {
2898   HWND hwndRichEdit;
2899   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2900   SCROLLINFO si;
2901   WNDCLASSA cls;
2902   BOOL r;
2903 
2904   /* These tests show that richedit should temporarily refrain from automatically
2905      hiding or showing its scrollbars (vertical at least) when an explicit request
2906      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2907      Some applications depend on forced showing (when otherwise richedit would
2908      hide the vertical scrollbar) and are thrown on an endless recursive loop
2909      if richedit auto-hides the scrollbar again. Apparently they never heard of
2910      the ES_DISABLENOSCROLL style... */
2911 
2912   hwndRichEdit = new_richedit(NULL);
2913 
2914   /* Test default scrollbar visibility behavior */
2915   memset(&si, 0, sizeof(si));
2916   si.cbSize = sizeof(si);
2917   si.fMask = SIF_PAGE | SIF_RANGE;
2918   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2920     "Vertical scrollbar is visible, should be invisible.\n");
2921   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923         si.nPage, si.nMin, si.nMax);
2924 
2925   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2926   memset(&si, 0, sizeof(si));
2927   si.cbSize = sizeof(si);
2928   si.fMask = SIF_PAGE | SIF_RANGE;
2929   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931     "Vertical scrollbar is visible, should be invisible.\n");
2932   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934         si.nPage, si.nMin, si.nMax);
2935 
2936   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2937   memset(&si, 0, sizeof(si));
2938   si.cbSize = sizeof(si);
2939   si.fMask = SIF_PAGE | SIF_RANGE;
2940   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2942     "Vertical scrollbar is invisible, should be visible.\n");
2943   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2944         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2945         si.nPage, si.nMin, si.nMax);
2946 
2947   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2948      even though it hides the scrollbar */
2949   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950   memset(&si, 0, sizeof(si));
2951   si.cbSize = sizeof(si);
2952   si.fMask = SIF_PAGE | SIF_RANGE;
2953   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955     "Vertical scrollbar is visible, should be invisible.\n");
2956   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958         si.nPage, si.nMin, si.nMax);
2959 
2960   /* Setting non-scrolling text again does *not* reset scrollbar range */
2961   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2962   memset(&si, 0, sizeof(si));
2963   si.cbSize = sizeof(si);
2964   si.fMask = SIF_PAGE | SIF_RANGE;
2965   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2966   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2967     "Vertical scrollbar is visible, should be invisible.\n");
2968   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2969         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2970         si.nPage, si.nMin, si.nMax);
2971 
2972   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2973   memset(&si, 0, sizeof(si));
2974   si.cbSize = sizeof(si);
2975   si.fMask = SIF_PAGE | SIF_RANGE;
2976   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2977   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2978     "Vertical scrollbar is visible, should be invisible.\n");
2979   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2980         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2981         si.nPage, si.nMin, si.nMax);
2982 
2983   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2984   memset(&si, 0, sizeof(si));
2985   si.cbSize = sizeof(si);
2986   si.fMask = SIF_PAGE | SIF_RANGE;
2987   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2988   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2989     "Vertical scrollbar is visible, should be invisible.\n");
2990   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2991         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2992         si.nPage, si.nMin, si.nMax);
2993 
2994   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2995   memset(&si, 0, sizeof(si));
2996   si.cbSize = sizeof(si);
2997   si.fMask = SIF_PAGE | SIF_RANGE;
2998   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2999   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3000     "Vertical scrollbar is visible, should be invisible.\n");
3001   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3002         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3003         si.nPage, si.nMin, si.nMax);
3004 
3005   DestroyWindow(hwndRichEdit);
3006 
3007   /* Test again, with ES_DISABLENOSCROLL style */
3008   hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
3009 
3010   /* Test default scrollbar visibility behavior */
3011   memset(&si, 0, sizeof(si));
3012   si.cbSize = sizeof(si);
3013   si.fMask = SIF_PAGE | SIF_RANGE;
3014   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016     "Vertical scrollbar is invisible, should be visible.\n");
3017   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3018         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3019         si.nPage, si.nMin, si.nMax);
3020 
3021   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3022   memset(&si, 0, sizeof(si));
3023   si.cbSize = sizeof(si);
3024   si.fMask = SIF_PAGE | SIF_RANGE;
3025   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3027     "Vertical scrollbar is invisible, should be visible.\n");
3028   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3029         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3030         si.nPage, si.nMin, si.nMax);
3031 
3032   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3033   memset(&si, 0, sizeof(si));
3034   si.cbSize = sizeof(si);
3035   si.fMask = SIF_PAGE | SIF_RANGE;
3036   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3038     "Vertical scrollbar is invisible, should be visible.\n");
3039   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3040         "reported page/range is %d (%d..%d)\n",
3041         si.nPage, si.nMin, si.nMax);
3042 
3043   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3044   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3045   memset(&si, 0, sizeof(si));
3046   si.cbSize = sizeof(si);
3047   si.fMask = SIF_PAGE | SIF_RANGE;
3048   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050     "Vertical scrollbar is invisible, should be visible.\n");
3051   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3052         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3053         si.nPage, si.nMin, si.nMax);
3054 
3055   /* Setting non-scrolling text again does *not* reset scrollbar range */
3056   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3057   memset(&si, 0, sizeof(si));
3058   si.cbSize = sizeof(si);
3059   si.fMask = SIF_PAGE | SIF_RANGE;
3060   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3061   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3062     "Vertical scrollbar is invisible, should be visible.\n");
3063   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3064         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3065         si.nPage, si.nMin, si.nMax);
3066 
3067   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3068   memset(&si, 0, sizeof(si));
3069   si.cbSize = sizeof(si);
3070   si.fMask = SIF_PAGE | SIF_RANGE;
3071   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3072   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3073     "Vertical scrollbar is invisible, should be visible.\n");
3074   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3075         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3076         si.nPage, si.nMin, si.nMax);
3077 
3078   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3079   memset(&si, 0, sizeof(si));
3080   si.cbSize = sizeof(si);
3081   si.fMask = SIF_PAGE | SIF_RANGE;
3082   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084     "Vertical scrollbar is invisible, should be visible.\n");
3085   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3086         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3087         si.nPage, si.nMin, si.nMax);
3088 
3089   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3090   memset(&si, 0, sizeof(si));
3091   si.cbSize = sizeof(si);
3092   si.fMask = SIF_PAGE | SIF_RANGE;
3093   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3094   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3095     "Vertical scrollbar is invisible, should be visible.\n");
3096   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3097         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3098         si.nPage, si.nMin, si.nMax);
3099 
3100   DestroyWindow(hwndRichEdit);
3101 
3102   /* Test behavior with explicit visibility request, using ShowScrollBar() */
3103   hwndRichEdit = new_richedit(NULL);
3104 
3105   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3106   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3107   memset(&si, 0, sizeof(si));
3108   si.cbSize = sizeof(si);
3109   si.fMask = SIF_PAGE | SIF_RANGE;
3110   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3112     "Vertical scrollbar is invisible, should be visible.\n");
3113   todo_wine {
3114   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3115         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3116         si.nPage, si.nMin, si.nMax);
3117   }
3118 
3119   /* Ditto, see above */
3120   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3121   memset(&si, 0, sizeof(si));
3122   si.cbSize = sizeof(si);
3123   si.fMask = SIF_PAGE | SIF_RANGE;
3124   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126     "Vertical scrollbar is invisible, should be visible.\n");
3127   todo_wine {
3128   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3129         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3130         si.nPage, si.nMin, si.nMax);
3131   }
3132 
3133   /* Ditto, see above */
3134   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3135   memset(&si, 0, sizeof(si));
3136   si.cbSize = sizeof(si);
3137   si.fMask = SIF_PAGE | SIF_RANGE;
3138   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3139   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3140     "Vertical scrollbar is invisible, should be visible.\n");
3141   todo_wine {
3142   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3143         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3144         si.nPage, si.nMin, si.nMax);
3145   }
3146 
3147   /* Ditto, see above */
3148   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3149   memset(&si, 0, sizeof(si));
3150   si.cbSize = sizeof(si);
3151   si.fMask = SIF_PAGE | SIF_RANGE;
3152   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3153   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3154     "Vertical scrollbar is invisible, should be visible.\n");
3155   todo_wine {
3156   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3157         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3158         si.nPage, si.nMin, si.nMax);
3159   }
3160 
3161   /* Ditto, see above */
3162   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3163   memset(&si, 0, sizeof(si));
3164   si.cbSize = sizeof(si);
3165   si.fMask = SIF_PAGE | SIF_RANGE;
3166   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3168     "Vertical scrollbar is invisible, should be visible.\n");
3169   todo_wine {
3170   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3171         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3172         si.nPage, si.nMin, si.nMax);
3173   }
3174 
3175   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3176   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3177   memset(&si, 0, sizeof(si));
3178   si.cbSize = sizeof(si);
3179   si.fMask = SIF_PAGE | SIF_RANGE;
3180   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3181   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3182     "Vertical scrollbar is visible, should be invisible.\n");
3183   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3184         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3185         si.nPage, si.nMin, si.nMax);
3186 
3187   DestroyWindow(hwndRichEdit);
3188 
3189   hwndRichEdit = new_richedit(NULL);
3190 
3191   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3192   memset(&si, 0, sizeof(si));
3193   si.cbSize = sizeof(si);
3194   si.fMask = SIF_PAGE | SIF_RANGE;
3195   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3196   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3197     "Vertical scrollbar is visible, should be invisible.\n");
3198   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3199         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3200         si.nPage, si.nMin, si.nMax);
3201 
3202   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3203   memset(&si, 0, sizeof(si));
3204   si.cbSize = sizeof(si);
3205   si.fMask = SIF_PAGE | SIF_RANGE;
3206   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3207   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3208     "Vertical scrollbar is visible, should be invisible.\n");
3209   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3210         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3211         si.nPage, si.nMin, si.nMax);
3212 
3213   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3214   memset(&si, 0, sizeof(si));
3215   si.cbSize = sizeof(si);
3216   si.fMask = SIF_PAGE | SIF_RANGE;
3217   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3218   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3219     "Vertical scrollbar is visible, should be invisible.\n");
3220   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3221         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3222         si.nPage, si.nMin, si.nMax);
3223 
3224   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3225   memset(&si, 0, sizeof(si));
3226   si.cbSize = sizeof(si);
3227   si.fMask = SIF_PAGE | SIF_RANGE;
3228   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3229   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3230     "Vertical scrollbar is visible, should be invisible.\n");
3231   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3232         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3233         si.nPage, si.nMin, si.nMax);
3234 
3235   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3236   memset(&si, 0, sizeof(si));
3237   si.cbSize = sizeof(si);
3238   si.fMask = SIF_PAGE | SIF_RANGE;
3239   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3240   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3241     "Vertical scrollbar is invisible, should be visible.\n");
3242   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3243         "reported page/range is %d (%d..%d)\n",
3244         si.nPage, si.nMin, si.nMax);
3245 
3246   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3247   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3248   memset(&si, 0, sizeof(si));
3249   si.cbSize = sizeof(si);
3250   si.fMask = SIF_PAGE | SIF_RANGE;
3251   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3252   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3253     "Vertical scrollbar is visible, should be invisible.\n");
3254   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3255         "reported page/range is %d (%d..%d)\n",
3256         si.nPage, si.nMin, si.nMax);
3257 
3258   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3259   memset(&si, 0, sizeof(si));
3260   si.cbSize = sizeof(si);
3261   si.fMask = SIF_PAGE | SIF_RANGE;
3262   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3263   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3264     "Vertical scrollbar is visible, should be invisible.\n");
3265   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3266         "reported page/range is %d (%d..%d)\n",
3267         si.nPage, si.nMin, si.nMax);
3268 
3269   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3270      EM_SCROLL will make visible any forcefully invisible scrollbar */
3271   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3272   memset(&si, 0, sizeof(si));
3273   si.cbSize = sizeof(si);
3274   si.fMask = SIF_PAGE | SIF_RANGE;
3275   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3276   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3277     "Vertical scrollbar is invisible, should be visible.\n");
3278   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3279         "reported page/range is %d (%d..%d)\n",
3280         si.nPage, si.nMin, si.nMax);
3281 
3282   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3283   memset(&si, 0, sizeof(si));
3284   si.cbSize = sizeof(si);
3285   si.fMask = SIF_PAGE | SIF_RANGE;
3286   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3287   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3288     "Vertical scrollbar is visible, should be invisible.\n");
3289   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3290         "reported page/range is %d (%d..%d)\n",
3291         si.nPage, si.nMin, si.nMax);
3292 
3293   /* Again, EM_SCROLL, with SB_LINEUP */
3294   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3295   memset(&si, 0, sizeof(si));
3296   si.cbSize = sizeof(si);
3297   si.fMask = SIF_PAGE | SIF_RANGE;
3298   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3299   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3300     "Vertical scrollbar is invisible, should be visible.\n");
3301   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3302         "reported page/range is %d (%d..%d)\n",
3303         si.nPage, si.nMin, si.nMax);
3304 
3305   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3306   memset(&si, 0, sizeof(si));
3307   si.cbSize = sizeof(si);
3308   si.fMask = SIF_PAGE | SIF_RANGE;
3309   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3310   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3311     "Vertical scrollbar is visible, should be invisible.\n");
3312   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3313         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3314         si.nPage, si.nMin, si.nMax);
3315 
3316   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3317   memset(&si, 0, sizeof(si));
3318   si.cbSize = sizeof(si);
3319   si.fMask = SIF_PAGE | SIF_RANGE;
3320   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3321   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3322     "Vertical scrollbar is invisible, should be visible.\n");
3323   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3324         "reported page/range is %d (%d..%d)\n",
3325         si.nPage, si.nMin, si.nMax);
3326 
3327   DestroyWindow(hwndRichEdit);
3328 
3329 
3330   /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3331   hwndRichEdit = new_richedit(NULL);
3332 
3333 #define ENABLE_WS_VSCROLL(hwnd) \
3334     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3335 #define DISABLE_WS_VSCROLL(hwnd) \
3336     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3337 
3338   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3339   ENABLE_WS_VSCROLL(hwndRichEdit);
3340   memset(&si, 0, sizeof(si));
3341   si.cbSize = sizeof(si);
3342   si.fMask = SIF_PAGE | SIF_RANGE;
3343   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3344   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3345     "Vertical scrollbar is invisible, should be visible.\n");
3346   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3347         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3348         si.nPage, si.nMin, si.nMax);
3349 
3350   /* Ditto, see above */
3351   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3352   memset(&si, 0, sizeof(si));
3353   si.cbSize = sizeof(si);
3354   si.fMask = SIF_PAGE | SIF_RANGE;
3355   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3356   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3357     "Vertical scrollbar is invisible, should be visible.\n");
3358   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3359         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3360         si.nPage, si.nMin, si.nMax);
3361 
3362   /* Ditto, see above */
3363   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3364   memset(&si, 0, sizeof(si));
3365   si.cbSize = sizeof(si);
3366   si.fMask = SIF_PAGE | SIF_RANGE;
3367   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3368   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3369     "Vertical scrollbar is invisible, should be visible.\n");
3370   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3371         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3372         si.nPage, si.nMin, si.nMax);
3373 
3374   /* Ditto, see above */
3375   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3376   memset(&si, 0, sizeof(si));
3377   si.cbSize = sizeof(si);
3378   si.fMask = SIF_PAGE | SIF_RANGE;
3379   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3380   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3381     "Vertical scrollbar is invisible, should be visible.\n");
3382   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3383         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3384         si.nPage, si.nMin, si.nMax);
3385 
3386   /* Ditto, see above */
3387   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3388   memset(&si, 0, sizeof(si));
3389   si.cbSize = sizeof(si);
3390   si.fMask = SIF_PAGE | SIF_RANGE;
3391   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3392   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3393     "Vertical scrollbar is invisible, should be visible.\n");
3394   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3395         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3396         si.nPage, si.nMin, si.nMax);
3397 
3398   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3399   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3400   memset(&si, 0, sizeof(si));
3401   si.cbSize = sizeof(si);
3402   si.fMask = SIF_PAGE | SIF_RANGE;
3403   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3404   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3405     "Vertical scrollbar is visible, should be invisible.\n");
3406   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3407         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3408         si.nPage, si.nMin, si.nMax);
3409 
3410   DestroyWindow(hwndRichEdit);
3411 
3412   hwndRichEdit = new_richedit(NULL);
3413 
3414   DISABLE_WS_VSCROLL(hwndRichEdit);
3415   memset(&si, 0, sizeof(si));
3416   si.cbSize = sizeof(si);
3417   si.fMask = SIF_PAGE | SIF_RANGE;
3418   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3419   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3420     "Vertical scrollbar is visible, should be invisible.\n");
3421   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3422         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3423         si.nPage, si.nMin, si.nMax);
3424 
3425   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3426   memset(&si, 0, sizeof(si));
3427   si.cbSize = sizeof(si);
3428   si.fMask = SIF_PAGE | SIF_RANGE;
3429   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3430   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3431     "Vertical scrollbar is visible, should be invisible.\n");
3432   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3433         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3434         si.nPage, si.nMin, si.nMax);
3435 
3436   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3437   memset(&si, 0, sizeof(si));
3438   si.cbSize = sizeof(si);
3439   si.fMask = SIF_PAGE | SIF_RANGE;
3440   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3441   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3442     "Vertical scrollbar is visible, should be invisible.\n");
3443   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3444         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3445         si.nPage, si.nMin, si.nMax);
3446 
3447   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3448   memset(&si, 0, sizeof(si));
3449   si.cbSize = sizeof(si);
3450   si.fMask = SIF_PAGE | SIF_RANGE;
3451   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3452   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3453     "Vertical scrollbar is visible, should be invisible.\n");
3454   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3455         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3456         si.nPage, si.nMin, si.nMax);
3457 
3458   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3459   memset(&si, 0, sizeof(si));
3460   si.cbSize = sizeof(si);
3461   si.fMask = SIF_PAGE | SIF_RANGE;
3462   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3463   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3464     "Vertical scrollbar is invisible, should be visible.\n");
3465   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3466         "reported page/range is %d (%d..%d)\n",
3467         si.nPage, si.nMin, si.nMax);
3468 
3469   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3470   DISABLE_WS_VSCROLL(hwndRichEdit);
3471   memset(&si, 0, sizeof(si));
3472   si.cbSize = sizeof(si);
3473   si.fMask = SIF_PAGE | SIF_RANGE;
3474   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3475   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3476     "Vertical scrollbar is visible, should be invisible.\n");
3477   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3478         "reported page/range is %d (%d..%d)\n",
3479         si.nPage, si.nMin, si.nMax);
3480 
3481   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3482   memset(&si, 0, sizeof(si));
3483   si.cbSize = sizeof(si);
3484   si.fMask = SIF_PAGE | SIF_RANGE;
3485   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3486   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3487     "Vertical scrollbar is visible, should be invisible.\n");
3488   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3489         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3490         si.nPage, si.nMin, si.nMax);
3491 
3492   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3493   memset(&si, 0, sizeof(si));
3494   si.cbSize = sizeof(si);
3495   si.fMask = SIF_PAGE | SIF_RANGE;
3496   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3497   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3498     "Vertical scrollbar is invisible, should be visible.\n");
3499   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3500         "reported page/range is %d (%d..%d)\n",
3501         si.nPage, si.nMin, si.nMax);
3502 
3503   DISABLE_WS_VSCROLL(hwndRichEdit);
3504   memset(&si, 0, sizeof(si));
3505   si.cbSize = sizeof(si);
3506   si.fMask = SIF_PAGE | SIF_RANGE;
3507   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3508   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3509     "Vertical scrollbar is visible, should be invisible.\n");
3510   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3511         "reported page/range is %d (%d..%d)\n",
3512         si.nPage, si.nMin, si.nMax);
3513 
3514   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3515      EM_SCROLL will make visible any forcefully invisible scrollbar */
3516   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3517   memset(&si, 0, sizeof(si));
3518   si.cbSize = sizeof(si);
3519   si.fMask = SIF_PAGE | SIF_RANGE;
3520   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3521   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3522     "Vertical scrollbar is invisible, should be visible.\n");
3523   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3524         "reported page/range is %d (%d..%d)\n",
3525         si.nPage, si.nMin, si.nMax);
3526 
3527   DISABLE_WS_VSCROLL(hwndRichEdit);
3528   memset(&si, 0, sizeof(si));
3529   si.cbSize = sizeof(si);
3530   si.fMask = SIF_PAGE | SIF_RANGE;
3531   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3532   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3533     "Vertical scrollbar is visible, should be invisible.\n");
3534   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3535         "reported page/range is %d (%d..%d)\n",
3536         si.nPage, si.nMin, si.nMax);
3537 
3538   /* Again, EM_SCROLL, with SB_LINEUP */
3539   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3540   memset(&si, 0, sizeof(si));
3541   si.cbSize = sizeof(si);
3542   si.fMask = SIF_PAGE | SIF_RANGE;
3543   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3544   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3545     "Vertical scrollbar is invisible, should be visible.\n");
3546   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3547         "reported page/range is %d (%d..%d)\n",
3548         si.nPage, si.nMin, si.nMax);
3549 
3550   DestroyWindow(hwndRichEdit);
3551 
3552   /* This window proc models what is going on with Corman Lisp 3.0.
3553      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3554      force the scrollbar into visibility. Recursion should NOT happen
3555      as a result of this action.
3556    */
3557   r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3558   if (r) {
3559     richeditProc = cls.lpfnWndProc;
3560     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3561     cls.lpszClassName = "RicheditStupidOverride";
3562     if(!RegisterClassA(&cls)) assert(0);
3563 
3564     recursionLevel = 0;
3565     WM_SIZE_recursionLevel = 0;
3566     bailedOutOfRecursion = FALSE;
3567     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3568     ok(!bailedOutOfRecursion,
3569         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3570 
3571     recursionLevel = 0;
3572     WM_SIZE_recursionLevel = 0;
3573     bailedOutOfRecursion = FALSE;
3574     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3575     ok(!bailedOutOfRecursion,
3576         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3577 
3578     /* Unblock window in order to process WM_DESTROY */
3579     recursionLevel = 0;
3580     bailedOutOfRecursion = FALSE;
3581     WM_SIZE_recursionLevel = 0;
3582     DestroyWindow(hwndRichEdit);
3583   }
3584 }
3585 
test_EM_SETUNDOLIMIT(void)3586 static void test_EM_SETUNDOLIMIT(void)
3587 {
3588   /* cases we test for:
3589    * default behaviour - limiting at 100 undo's
3590    * undo disabled - setting a limit of 0
3591    * undo limited -  undo limit set to some to some number, like 2
3592    * bad input - sending a negative number should default to 100 undo's */
3593 
3594   HWND hwndRichEdit = new_richedit(NULL);
3595   CHARRANGE cr;
3596   int i;
3597   int result;
3598 
3599   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3600   cr.cpMin = 0;
3601   cr.cpMax = -1;
3602   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3603 
3604   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3605     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3606       also, multiple pastes don't combine like WM_CHAR would */
3607 
3608   /* first case - check the default */
3609   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3610   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3611     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3612   for (i=0; i<100; i++) /* Undo 100 of them */
3613     SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3614   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3615      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3616 
3617   /* second case - cannot undo */
3618   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3619   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3620   SendMessageA(hwndRichEdit,
3621               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3622   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3623      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3624 
3625   /* third case - set it to an arbitrary number */
3626   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3627   SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3628   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3629   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3630   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3631   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3632   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3633      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3634   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3635   ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3636      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3637   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3638   ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3639      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3640 
3641   /* fourth case - setting negative numbers should default to 100 undos */
3642   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3643   result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3644   ok (result == 100,
3645       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3646 
3647   DestroyWindow(hwndRichEdit);
3648 }
3649 
test_ES_PASSWORD(void)3650 static void test_ES_PASSWORD(void)
3651 {
3652   /* This isn't hugely testable, so we're just going to run it through its paces */
3653 
3654   HWND hwndRichEdit = new_richedit(NULL);
3655   WCHAR result;
3656 
3657   /* First, check the default of a regular control */
3658   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3659   ok (result == 0,
3660 	"EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3661 
3662   /* Now, set it to something normal */
3663   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3664   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3665   ok (result == 120,
3666 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3667 
3668   /* Now, set it to something odd */
3669   SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3670   result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3671   ok (result == 1234,
3672 	"EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3673   DestroyWindow(hwndRichEdit);
3674 }
3675 
3676 LONG streamout_written = 0;
3677 
test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)3678 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3679                                          LPBYTE pbBuff,
3680                                          LONG cb,
3681                                          LONG *pcb)
3682 {
3683   char** str = (char**)dwCookie;
3684   *pcb = cb;
3685   if (*pcb > 0) {
3686     memcpy(*str, pbBuff, *pcb);
3687     *str += *pcb;
3688   }
3689   streamout_written = *pcb;
3690   return 0;
3691 }
3692 
test_WM_SETTEXT(void)3693 static void test_WM_SETTEXT(void)
3694 {
3695   HWND hwndRichEdit = new_richedit(NULL);
3696   const char * TestItem1 = "TestSomeText";
3697   const char * TestItem2 = "TestSomeText\r";
3698   const char * TestItem2_after = "TestSomeText\r\n";
3699   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3700   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3701   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3702   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3703   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3704   const char * TestItem5_after = "TestSomeText TestSomeText";
3705   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3706   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3707   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3708   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3709 
3710   const char rtftextA[] = "{\\rtf sometext}";
3711   const char urtftextA[] = "{\\urtf sometext}";
3712   const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3713   const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3714   const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3715 
3716   char buf[1024] = {0};
3717   WCHAR bufW[1024] = {0};
3718   LRESULT result;
3719 
3720   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3721      any solitary \r to be converted to \r\n on return. Properly paired
3722      \r\n are not affected. It also shows that the special sequence \r\r\n
3723      gets converted to a single space.
3724    */
3725 
3726 #define TEST_SETTEXT(a, b) \
3727   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3728   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3729   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3730   ok (result == lstrlenA(buf), \
3731 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3732 	result, lstrlenA(buf)); \
3733   result = strcmp(b, buf); \
3734   ok(result == 0, \
3735         "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3736 
3737   TEST_SETTEXT(TestItem1, TestItem1)
3738   TEST_SETTEXT(TestItem2, TestItem2_after)
3739   TEST_SETTEXT(TestItem3, TestItem3_after)
3740   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3741   TEST_SETTEXT(TestItem4, TestItem4_after)
3742   TEST_SETTEXT(TestItem5, TestItem5_after)
3743   TEST_SETTEXT(TestItem6, TestItem6_after)
3744   TEST_SETTEXT(TestItem7, TestItem7_after)
3745 
3746   /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3747   TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3748   TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3749   TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3750   TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3751   DestroyWindow(hwndRichEdit);
3752 #undef TEST_SETTEXT
3753 
3754 #define TEST_SETTEXTW(a, b) \
3755   result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3756   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3757   result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3758   ok (result == lstrlenW(bufW), \
3759 	"WM_GETTEXT returned %ld instead of expected %u\n", \
3760 	result, lstrlenW(bufW)); \
3761   result = lstrcmpW(b, bufW); \
3762   ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3763 
3764   hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3765                                ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3766                                0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3767   ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3768   TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3769   TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3770   TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3771   TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3772   DestroyWindow(hwndRichEdit);
3773 #undef TEST_SETTEXTW
3774 
3775   /* Single-line richedit */
3776   hwndRichEdit = new_richedit_with_style(NULL, 0);
3777   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3778   ok(result == 1, "WM_SETTEXT returned %ld, expected 12\n", result);
3779   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3780   ok(result == 5, "WM_GETTEXT returned %ld, expected 5\n", result);
3781   ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3782   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3783   ok(result == 1, "WM_SETTEXT returned %ld, expected 1\n", result);
3784   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3785   ok(result == 3, "WM_GETTEXT returned %ld, expected 3\n", result);
3786   ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3787   DestroyWindow(hwndRichEdit);
3788 }
3789 
3790 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3791    resent to the callkack. */
test_esCallback_written_1(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)3792 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3793                                                 LPBYTE pbBuff,
3794                                                 LONG cb,
3795                                                 LONG *pcb)
3796 {
3797   char** str = (char**)dwCookie;
3798   ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3799   *pcb = 0;
3800   if (cb > 0) {
3801     memcpy(*str, pbBuff, cb);
3802     *str += cb;
3803     *pcb = 1;
3804   }
3805   return 0;
3806 }
3807 
count_pars(const char * buf)3808 static int count_pars(const char *buf)
3809 {
3810     const char *p = buf;
3811     int count = 0;
3812     while ((p = strstr( p, "\\par" )) != NULL)
3813     {
3814         if (!isalpha( p[4] ))
3815            count++;
3816         p++;
3817     }
3818     return count;
3819 }
3820 
test_EM_STREAMOUT(void)3821 static void test_EM_STREAMOUT(void)
3822 {
3823   HWND hwndRichEdit = new_richedit(NULL);
3824   int r;
3825   EDITSTREAM es;
3826   char buf[1024] = {0};
3827   char * p;
3828   LRESULT result;
3829 
3830   const char * TestItem1 = "TestSomeText";
3831   const char * TestItem2 = "TestSomeText\r";
3832   const char * TestItem3 = "TestSomeText\r\n";
3833 
3834   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3835   p = buf;
3836   es.dwCookie = (DWORD_PTR)&p;
3837   es.dwError = 0;
3838   es.pfnCallback = test_WM_SETTEXT_esCallback;
3839   memset(buf, 0, sizeof(buf));
3840   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3841   r = strlen(buf);
3842   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3843   ok(strcmp(buf, TestItem1) == 0,
3844         "streamed text different, got %s\n", buf);
3845   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3846 
3847   /* RTF mode writes the final end of para \r if it's part of the selection */
3848   p = buf;
3849   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3850   ok (count_pars(buf) == 1, "got %s\n", buf);
3851   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3852   p = buf;
3853   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3854   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3855   ok (count_pars(buf) == 0, "got %s\n", buf);
3856   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3857   p = buf;
3858   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3859   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3860   ok (count_pars(buf) == 1, "got %s\n", buf);
3861   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3862 
3863   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3864   p = buf;
3865   es.dwCookie = (DWORD_PTR)&p;
3866   es.dwError = 0;
3867   es.pfnCallback = test_WM_SETTEXT_esCallback;
3868   memset(buf, 0, sizeof(buf));
3869   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3870   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3871   r = strlen(buf);
3872   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3873   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3874   ok(strcmp(buf, TestItem3) == 0,
3875         "streamed text different from, got %s\n", buf);
3876 
3877   /* And again RTF mode writes the final end of para \r if it's part of the selection */
3878   p = buf;
3879   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3880   ok (count_pars(buf) == 2, "got %s\n", buf);
3881   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3882   p = buf;
3883   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3884   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3885   ok (count_pars(buf) == 1, "got %s\n", buf);
3886   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3887   p = buf;
3888   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3889   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3890   ok (count_pars(buf) == 2, "got %s\n", buf);
3891   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3892 
3893   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3894   p = buf;
3895   es.dwCookie = (DWORD_PTR)&p;
3896   es.dwError = 0;
3897   es.pfnCallback = test_WM_SETTEXT_esCallback;
3898   memset(buf, 0, sizeof(buf));
3899   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3900   ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3901   r = strlen(buf);
3902   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3903   ok(strcmp(buf, TestItem3) == 0,
3904         "streamed text different, got %s\n", buf);
3905 
3906   /* Use a callback that sets *pcb to one */
3907   p = buf;
3908   es.dwCookie = (DWORD_PTR)&p;
3909   es.dwError = 0;
3910   es.pfnCallback = test_esCallback_written_1;
3911   memset(buf, 0, sizeof(buf));
3912   result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3913   r = strlen(buf);
3914   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3915   ok(strcmp(buf, TestItem3) == 0,
3916         "streamed text different, got %s\n", buf);
3917   ok(result == 0, "got %ld expected 0\n", result);
3918 
3919 
3920   DestroyWindow(hwndRichEdit);
3921 }
3922 
test_EM_STREAMOUT_FONTTBL(void)3923 static void test_EM_STREAMOUT_FONTTBL(void)
3924 {
3925   HWND hwndRichEdit = new_richedit(NULL);
3926   EDITSTREAM es;
3927   char buf[1024] = {0};
3928   char * p;
3929   char * fontTbl;
3930   int brackCount;
3931 
3932   const char * TestItem = "TestSomeText";
3933 
3934   /* fills in the richedit control with some text */
3935   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3936 
3937   /* streams out the text in rtf format */
3938   p = buf;
3939   es.dwCookie = (DWORD_PTR)&p;
3940   es.dwError = 0;
3941   es.pfnCallback = test_WM_SETTEXT_esCallback;
3942   memset(buf, 0, sizeof(buf));
3943   SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3944 
3945   /* scans for \fonttbl, error if not found */
3946   fontTbl = strstr(buf, "\\fonttbl");
3947   ok(fontTbl != NULL, "missing \\fonttbl section\n");
3948   if(fontTbl)
3949   {
3950       /* scans for terminating closing bracket */
3951       brackCount = 1;
3952       while(*fontTbl && brackCount)
3953       {
3954           if(*fontTbl == '{')
3955               brackCount++;
3956           else if(*fontTbl == '}')
3957               brackCount--;
3958           fontTbl++;
3959       }
3960     /* checks whether closing bracket is ok */
3961       ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3962       if(!brackCount)
3963       {
3964           /* char before closing fonttbl block should be a closed bracket */
3965           fontTbl -= 2;
3966           ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3967 
3968           /* char after fonttbl block should be a crlf */
3969           fontTbl += 2;
3970           ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3971       }
3972   }
3973   DestroyWindow(hwndRichEdit);
3974 }
3975 
test_EM_STREAMOUT_empty_para(void)3976 static void test_EM_STREAMOUT_empty_para(void)
3977 {
3978     HWND hwnd = new_richedit(NULL);
3979     char buf[1024], *p = buf;
3980     EDITSTREAM es;
3981 
3982     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
3983 
3984     memset(buf, 0, sizeof(buf));
3985     es.dwCookie    = (DWORD_PTR)&p;
3986     es.dwError     = 0;
3987     es.pfnCallback = test_WM_SETTEXT_esCallback;
3988 
3989     SendMessageA(hwnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3990     ok((p = strstr(buf, "\\pard")) != NULL, "missing \\pard\n");
3991     ok(((p = strstr(p, "\\fs")) && isdigit(p[3])), "missing \\fs\n");
3992 
3993     DestroyWindow(hwnd);
3994 }
3995 
test_EM_SETTEXTEX(void)3996 static void test_EM_SETTEXTEX(void)
3997 {
3998   HWND hwndRichEdit, parent;
3999   SCROLLINFO si;
4000   int sel_start, sel_end;
4001   SETTEXTEX setText;
4002   GETTEXTEX getText;
4003   WCHAR TestItem1[] = {'T', 'e', 's', 't',
4004                        'S', 'o', 'm', 'e',
4005                        'T', 'e', 'x', 't', 0};
4006   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
4007                           't', 'S', 'o', 'm',
4008                           'e', 'T', 'e', 'x',
4009                           't', 't', 'S', 'o',
4010                           'm', 'e', 'T', 'e',
4011                           'x', 't', 0};
4012   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
4013                            '\r','t','S','o','m','e','T','e','x','t',0};
4014   WCHAR TestItem2[] = {'T', 'e', 's', 't',
4015                        'S', 'o', 'm', 'e',
4016                        'T', 'e', 'x', 't',
4017                       '\r', 0};
4018   const char * TestItem2_after = "TestSomeText\r\n";
4019   WCHAR TestItem3[] = {'T', 'e', 's', 't',
4020                        'S', 'o', 'm', 'e',
4021                        'T', 'e', 'x', 't',
4022                       '\r','\n','\r','\n', 0};
4023   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
4024                        'S', 'o', 'm', 'e',
4025                        'T', 'e', 'x', 't',
4026                        '\n','\n', 0};
4027   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
4028                        'S', 'o', 'm', 'e',
4029                        'T', 'e', 'x', 't',
4030                        '\r','\r', 0};
4031   WCHAR TestItem4[] = {'T', 'e', 's', 't',
4032                        'S', 'o', 'm', 'e',
4033                        'T', 'e', 'x', 't',
4034                       '\r','\r','\n','\r',
4035                       '\n', 0};
4036   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
4037                        'S', 'o', 'm', 'e',
4038                        'T', 'e', 'x', 't',
4039                        ' ','\r', 0};
4040 #define MAX_BUF_LEN 1024
4041   WCHAR buf[MAX_BUF_LEN];
4042   char bufACP[MAX_BUF_LEN];
4043   char * p;
4044   int result;
4045   CHARRANGE cr;
4046   EDITSTREAM es;
4047   WNDCLASSA cls;
4048 
4049   /* Test the scroll position with and without a parent window.
4050    *
4051    * For some reason the scroll position is 0 after EM_SETTEXTEX
4052    * with the ST_SELECTION flag only when the control has a parent
4053    * window, even though the selection is at the end. */
4054   cls.style = 0;
4055   cls.lpfnWndProc = DefWindowProcA;
4056   cls.cbClsExtra = 0;
4057   cls.cbWndExtra = 0;
4058   cls.hInstance = GetModuleHandleA(0);
4059   cls.hIcon = 0;
4060   cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
4061   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4062   cls.lpszMenuName = NULL;
4063   cls.lpszClassName = "ParentTestClass";
4064   if(!RegisterClassA(&cls)) assert(0);
4065 
4066   parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4067                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
4068   ok (parent != 0, "Failed to create parent window\n");
4069 
4070   hwndRichEdit = CreateWindowExA(0,
4071                         RICHEDIT_CLASS20A, NULL,
4072                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4073                         0, 0, 200, 60, parent, NULL,
4074                         hmoduleRichEdit, NULL);
4075 
4076   setText.codepage = CP_ACP;
4077   setText.flags = ST_SELECTION;
4078   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4079                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4080   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4081   si.cbSize = sizeof(si);
4082   si.fMask = SIF_ALL;
4083   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4084   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4085   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4086   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4087   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4088 
4089   DestroyWindow(parent);
4090 
4091   /* Test without a parent window */
4092   hwndRichEdit = new_richedit(NULL);
4093   setText.codepage = CP_ACP;
4094   setText.flags = ST_SELECTION;
4095   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4096                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4097   todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4098   si.cbSize = sizeof(si);
4099   si.fMask = SIF_ALL;
4100   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4101   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4102   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4103   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4104   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4105 
4106   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4107    * but this time it is because the selection is at the beginning. */
4108   setText.codepage = CP_ACP;
4109   setText.flags = ST_DEFAULT;
4110   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4111                         (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4112   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4113   si.cbSize = sizeof(si);
4114   si.fMask = SIF_ALL;
4115   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4116   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4117   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4118   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4119   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4120 
4121   setText.codepage = 1200;  /* no constant for unicode */
4122   getText.codepage = 1200;  /* no constant for unicode */
4123   getText.cb = MAX_BUF_LEN;
4124   getText.flags = GT_DEFAULT;
4125   getText.lpDefaultChar = NULL;
4126   getText.lpUsedDefChar = NULL;
4127 
4128   setText.flags = 0;
4129   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4130   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4131   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4132   ok(lstrcmpW(buf, TestItem1) == 0,
4133       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4134 
4135   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4136      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4137    */
4138   setText.codepage = 1200;  /* no constant for unicode */
4139   getText.codepage = 1200;  /* no constant for unicode */
4140   getText.cb = MAX_BUF_LEN;
4141   getText.flags = GT_DEFAULT;
4142   getText.lpDefaultChar = NULL;
4143   getText.lpUsedDefChar = NULL;
4144   setText.flags = 0;
4145   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4146   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4147   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4148   ok(lstrcmpW(buf, TestItem2) == 0,
4149       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4150 
4151   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4152   SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4153   ok(strcmp((const char *)buf, TestItem2_after) == 0,
4154       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4155 
4156   /* Baseline test for just-enough buffer space for string */
4157   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4158   getText.codepage = 1200;  /* no constant for unicode */
4159   getText.flags = GT_DEFAULT;
4160   getText.lpDefaultChar = NULL;
4161   getText.lpUsedDefChar = NULL;
4162   memset(buf, 0, sizeof(buf));
4163   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4164   ok(lstrcmpW(buf, TestItem2) == 0,
4165       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4166 
4167   /* When there is enough space for one character, but not both, of the CRLF
4168      pair at the end of the string, the CR is not copied at all. That is,
4169      the caller must not see CRLF pairs truncated to CR at the end of the
4170      string.
4171    */
4172   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4173   getText.codepage = 1200;  /* no constant for unicode */
4174   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
4175   getText.lpDefaultChar = NULL;
4176   getText.lpUsedDefChar = NULL;
4177   memset(buf, 0, sizeof(buf));
4178   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4179   ok(lstrcmpW(buf, TestItem1) == 0,
4180       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4181 
4182 
4183   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4184   setText.codepage = 1200;  /* no constant for unicode */
4185   getText.codepage = 1200;  /* no constant for unicode */
4186   getText.cb = MAX_BUF_LEN;
4187   getText.flags = GT_DEFAULT;
4188   getText.lpDefaultChar = NULL;
4189   getText.lpUsedDefChar = NULL;
4190   setText.flags = 0;
4191   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4192   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4193   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4194   ok(lstrcmpW(buf, TestItem3_after) == 0,
4195       "EM_SETTEXTEX did not convert properly\n");
4196 
4197   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4198   setText.codepage = 1200;  /* no constant for unicode */
4199   getText.codepage = 1200;  /* no constant for unicode */
4200   getText.cb = MAX_BUF_LEN;
4201   getText.flags = GT_DEFAULT;
4202   getText.lpDefaultChar = NULL;
4203   getText.lpUsedDefChar = NULL;
4204   setText.flags = 0;
4205   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4206   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4207   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4208   ok(lstrcmpW(buf, TestItem3_after) == 0,
4209       "EM_SETTEXTEX did not convert properly\n");
4210 
4211   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4212   setText.codepage = 1200;  /* no constant for unicode */
4213   getText.codepage = 1200;  /* no constant for unicode */
4214   getText.cb = MAX_BUF_LEN;
4215   getText.flags = GT_DEFAULT;
4216   getText.lpDefaultChar = NULL;
4217   getText.lpUsedDefChar = NULL;
4218   setText.flags = 0;
4219   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4220   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4221   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4222   ok(lstrcmpW(buf, TestItem4_after) == 0,
4223       "EM_SETTEXTEX did not convert properly\n");
4224 
4225   /* !ST_SELECTION && Unicode && !\rtf */
4226   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4227   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4228 
4229   ok (result == 1,
4230       "EM_SETTEXTEX returned %d, instead of 1\n",result);
4231   ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4232 
4233   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4234   setText.flags = 0;
4235   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4236   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4237   /* select some text */
4238   cr.cpMax = 1;
4239   cr.cpMin = 3;
4240   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4241   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4242   setText.flags = ST_SELECTION;
4243   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4244   ok(result == 0,
4245       "EM_SETTEXTEX with NULL lParam to replace selection"
4246       " with no text should return 0. Got %i\n",
4247       result);
4248 
4249   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4250   setText.flags = 0;
4251   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4252   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4253   /* select some text */
4254   cr.cpMax = 1;
4255   cr.cpMin = 3;
4256   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4257   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4258   setText.flags = ST_SELECTION;
4259   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4260   /* get text */
4261   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4262   ok(result == lstrlenW(TestItem1),
4263       "EM_SETTEXTEX with NULL lParam to replace selection"
4264       " with no text should return 0. Got %i\n",
4265       result);
4266   ok(lstrlenW(buf) == 22,
4267       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4268       lstrlenW(buf) );
4269 
4270   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4271   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4272   p = (char *)buf;
4273   es.dwCookie = (DWORD_PTR)&p;
4274   es.dwError = 0;
4275   es.pfnCallback = test_WM_SETTEXT_esCallback;
4276   memset(buf, 0, sizeof(buf));
4277   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4278               (WPARAM)(SF_RTF), (LPARAM)&es);
4279   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4280 
4281   /* !ST_SELECTION && !Unicode && \rtf */
4282   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4283   getText.codepage = 1200;  /* no constant for unicode */
4284   getText.cb = MAX_BUF_LEN;
4285   getText.flags = GT_DEFAULT;
4286   getText.lpDefaultChar = NULL;
4287   getText.lpUsedDefChar = NULL;
4288 
4289   setText.flags = 0;
4290   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4291   ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4292   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4293   ok(lstrcmpW(buf, TestItem1) == 0,
4294       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4295 
4296   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4297    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4298   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4299   getText.codepage = CP_ACP;
4300   getText.cb = MAX_BUF_LEN;
4301   getText.flags = GT_DEFAULT;
4302   getText.lpDefaultChar = NULL;
4303   getText.lpUsedDefChar = NULL;
4304 
4305   setText.flags = ST_SELECTION;
4306   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4307   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4308   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4309   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4310   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4311 
4312   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4313   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4314   p = (char *)buf;
4315   es.dwCookie = (DWORD_PTR)&p;
4316   es.dwError = 0;
4317   es.pfnCallback = test_WM_SETTEXT_esCallback;
4318   memset(buf, 0, sizeof(buf));
4319   SendMessageA(hwndRichEdit, EM_STREAMOUT,
4320               (WPARAM)(SF_RTF), (LPARAM)&es);
4321   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4322 
4323   /* select some text */
4324   cr.cpMax = 1;
4325   cr.cpMin = 3;
4326   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4327 
4328   /* ST_SELECTION && !Unicode && \rtf */
4329   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4330   getText.codepage = 1200;  /* no constant for unicode */
4331   getText.cb = MAX_BUF_LEN;
4332   getText.flags = GT_DEFAULT;
4333   getText.lpDefaultChar = NULL;
4334   getText.lpUsedDefChar = NULL;
4335 
4336   setText.flags = ST_SELECTION;
4337   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4338   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4339   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4340 
4341   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4342   setText.codepage = 1200;  /* no constant for unicode */
4343   getText.codepage = CP_ACP;
4344   getText.cb = MAX_BUF_LEN;
4345 
4346   setText.flags = 0;
4347   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4348   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4349 
4350   /* select some text */
4351   cr.cpMax = 1;
4352   cr.cpMin = 3;
4353   SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4354 
4355   /* ST_SELECTION && !Unicode && !\rtf */
4356   setText.codepage = CP_ACP;
4357   getText.codepage = 1200;  /* no constant for unicode */
4358   getText.cb = MAX_BUF_LEN;
4359   getText.flags = GT_DEFAULT;
4360   getText.lpDefaultChar = NULL;
4361   getText.lpUsedDefChar = NULL;
4362 
4363   setText.flags = ST_SELECTION;
4364   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4365   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4366   ok(lstrcmpW(buf, TestItem1alt) == 0,
4367       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4368       " using ST_SELECTION and non-Unicode\n");
4369 
4370   /* Test setting text using rich text format */
4371   setText.flags = 0;
4372   setText.codepage = CP_ACP;
4373   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4374   getText.codepage = CP_ACP;
4375   getText.cb = MAX_BUF_LEN;
4376   getText.flags = GT_DEFAULT;
4377   getText.lpDefaultChar = NULL;
4378   getText.lpUsedDefChar = NULL;
4379   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4380   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4381 
4382   setText.flags = 0;
4383   setText.codepage = CP_ACP;
4384   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4385   getText.codepage = CP_ACP;
4386   getText.cb = MAX_BUF_LEN;
4387   getText.flags = GT_DEFAULT;
4388   getText.lpDefaultChar = NULL;
4389   getText.lpUsedDefChar = NULL;
4390   SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4391   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4392 
4393   /* test for utf8 text with BOM */
4394   setText.flags = 0;
4395   setText.codepage = CP_ACP;
4396   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4397   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4398   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4399   result = strcmp(bufACP, "TestUTF8WithBOM");
4400   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4401 
4402   setText.flags = 0;
4403   setText.codepage = CP_UTF8;
4404   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4405   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4406   ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4407   result = strcmp(bufACP, "TestUTF8WithBOM");
4408   ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4409 
4410   /* Test multibyte character */
4411   if (!is_lang_japanese)
4412     skip("Skip multibyte character tests on non-Japanese platform\n");
4413   else
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)"abc\x8e\xf0");
4419     todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\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     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4423        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4424 
4425     setText.flags = ST_DEFAULT;
4426     setText.codepage = CP_ACP;
4427     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4428     ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4429     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4430     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4431     ok(!strcmp(bufACP, "abc\x8e\xf0"),
4432        "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4433 
4434     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4435     setText.flags = ST_SELECTION;
4436     setText.codepage = CP_ACP;
4437     result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4438     todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4439     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4440     ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4441     todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4442                  "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4443   }
4444 
4445   DestroyWindow(hwndRichEdit);
4446 
4447   /* Single-line richedit */
4448   hwndRichEdit = new_richedit_with_style(NULL, 0);
4449   setText.flags = ST_DEFAULT;
4450   setText.codepage = CP_ACP;
4451   result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"line1\r\nline2");
4452   ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4453   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4454   ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4455   ok(!strcmp(bufACP, "line1"), "EM_SETTEXTEX: Test single-line text: Result: %s\n", bufACP);
4456   DestroyWindow(hwndRichEdit);
4457 }
4458 
test_EM_LIMITTEXT(void)4459 static void test_EM_LIMITTEXT(void)
4460 {
4461   int ret;
4462 
4463   HWND hwndRichEdit = new_richedit(NULL);
4464 
4465   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4466    * about setting the length to -1 for multiline edit controls doesn't happen.
4467    */
4468 
4469   /* Don't check default gettextlimit case. That's done in other tests */
4470 
4471   /* Set textlimit to 100 */
4472   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4473   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4474   ok (ret == 100,
4475       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4476 
4477   /* Set textlimit to 0 */
4478   SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4479   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4480   ok (ret == 65536,
4481       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4482 
4483   /* Set textlimit to -1 */
4484   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4485   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4486   ok (ret == -1,
4487       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4488 
4489   /* Set textlimit to -2 */
4490   SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4491   ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4492   ok (ret == -2,
4493       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4494 
4495   DestroyWindow (hwndRichEdit);
4496 }
4497 
4498 
test_EM_EXLIMITTEXT(void)4499 static void test_EM_EXLIMITTEXT(void)
4500 {
4501   int i, selBegin, selEnd, len1, len2;
4502   int result;
4503   char text[1024 + 1];
4504   char buffer[1024 + 1];
4505   int textlimit = 0; /* multiple of 100 */
4506   HWND hwndRichEdit = new_richedit(NULL);
4507 
4508   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4509   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4510 
4511   textlimit = 256000;
4512   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4513   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4514   /* set higher */
4515   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4516 
4517   textlimit = 1000;
4518   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4519   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4520   /* set lower */
4521   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4522 
4523   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4524   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4525   /* default for WParam = 0 */
4526   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4527 
4528   textlimit = sizeof(text)-1;
4529   memset(text, 'W', textlimit);
4530   text[sizeof(text)-1] = 0;
4531   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4532   /* maxed out text */
4533   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4534 
4535   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4536   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4537   len1 = selEnd - selBegin;
4538 
4539   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4540   SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4541   SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4542   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4543   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4544   len2 = selEnd - selBegin;
4545 
4546   ok(len1 != len2,
4547     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4548     len1,len2,i);
4549 
4550   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4551   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4552   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4553   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4554   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4555   len1 = selEnd - selBegin;
4556 
4557   ok(len1 != len2,
4558     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4559     len1,len2,i);
4560 
4561   SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4562   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4563   SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
4564   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4565   SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4566   len2 = selEnd - selBegin;
4567 
4568   ok(len1 == len2,
4569     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4570     len1,len2,i);
4571 
4572   /* set text up to the limit, select all the text, then add a char */
4573   textlimit = 5;
4574   memset(text, 'W', textlimit);
4575   text[textlimit] = 0;
4576   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4577   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4578   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4579   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4580   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4581   result = strcmp(buffer, "A");
4582   ok(0 == result, "got string = \"%s\"\n", buffer);
4583 
4584   /* WM_SETTEXT not limited */
4585   textlimit = 10;
4586   memset(text, 'W', textlimit);
4587   text[textlimit] = 0;
4588   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4589   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4590   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4591   i = strlen(buffer);
4592   ok(10 == i, "expected 10 chars\n");
4593   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4594   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4595 
4596   /* try inserting more text at end */
4597   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4598   ok(0 == i, "WM_CHAR wasn't processed\n");
4599   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4600   i = strlen(buffer);
4601   ok(10 == i, "expected 10 chars, got %i\n", i);
4602   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4603   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4604 
4605   /* try inserting text at beginning */
4606   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4607   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4608   ok(0 == i, "WM_CHAR wasn't processed\n");
4609   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4610   i = strlen(buffer);
4611   ok(10 == i, "expected 10 chars, got %i\n", i);
4612   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4613   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4614 
4615   /* WM_CHAR is limited */
4616   textlimit = 1;
4617   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4618   SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4619   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4620   ok(0 == i, "WM_CHAR wasn't processed\n");
4621   i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4622   ok(0 == i, "WM_CHAR wasn't processed\n");
4623   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4624   i = strlen(buffer);
4625   ok(1 == i, "expected 1 chars, got %i instead\n", i);
4626 
4627   DestroyWindow(hwndRichEdit);
4628 }
4629 
test_EM_GETLIMITTEXT(void)4630 static void test_EM_GETLIMITTEXT(void)
4631 {
4632   int i;
4633   HWND hwndRichEdit = new_richedit(NULL);
4634 
4635   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4636   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4637 
4638   SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4639   i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4640   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4641 
4642   DestroyWindow(hwndRichEdit);
4643 }
4644 
test_WM_SETFONT(void)4645 static void test_WM_SETFONT(void)
4646 {
4647   /* There is no invalid input or error conditions for this function.
4648    * NULL wParam and lParam just fall back to their default values
4649    * It should be noted that even if you use a gibberish name for your fonts
4650    * here, it will still work because the name is stored. They will display as
4651    * System, but will report their name to be whatever they were created as */
4652 
4653   HWND hwndRichEdit = new_richedit(NULL);
4654   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4655     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4656     FF_DONTCARE, "Marlett");
4657   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4658     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4659     FF_DONTCARE, "MS Sans Serif");
4660   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4661     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4662     FF_DONTCARE, "Courier");
4663   LOGFONTA sentLogFont;
4664   CHARFORMAT2A returnedCF2A;
4665 
4666   returnedCF2A.cbSize = sizeof(returnedCF2A);
4667 
4668   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4669   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4670   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4671 
4672   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4673   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4674     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4675     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4676 
4677   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4678   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4679   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4680   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4681     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4682     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4683 
4684   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4685   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4686   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4687   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4688     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4689     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4690 
4691   /* This last test is special since we send in NULL. We clear the variables
4692    * and just compare to "System" instead of the sent in font name. */
4693   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4694   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4695   returnedCF2A.cbSize = sizeof(returnedCF2A);
4696 
4697   SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4698   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM)&returnedCF2A);
4699   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4700   ok (!strcmp("System",returnedCF2A.szFaceName),
4701     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4702 
4703   DestroyWindow(hwndRichEdit);
4704 }
4705 
4706 
test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)4707 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4708                                          LPBYTE pbBuff,
4709                                          LONG cb,
4710                                          LONG *pcb)
4711 {
4712   const char** str = (const char**)dwCookie;
4713   int size = strlen(*str);
4714   if(size > 3)  /* let's make it piecemeal for fun */
4715     size = 3;
4716   *pcb = cb;
4717   if (*pcb > size) {
4718     *pcb = size;
4719   }
4720   if (*pcb > 0) {
4721     memcpy(pbBuff, *str, *pcb);
4722     *str += *pcb;
4723   }
4724   return 0;
4725 }
4726 
test_EM_GETMODIFY(void)4727 static void test_EM_GETMODIFY(void)
4728 {
4729   HWND hwndRichEdit = new_richedit(NULL);
4730   LRESULT result;
4731   SETTEXTEX setText;
4732   WCHAR TestItem1[] = {'T', 'e', 's', 't',
4733                        'S', 'o', 'm', 'e',
4734                        'T', 'e', 'x', 't', 0};
4735   WCHAR TestItem2[] = {'T', 'e', 's', 't',
4736                        'S', 'o', 'm', 'e',
4737                        'O', 't', 'h', 'e', 'r',
4738                        'T', 'e', 'x', 't', 0};
4739   const char* streamText = "hello world";
4740   CHARFORMAT2A cf2;
4741   PARAFORMAT2 pf2;
4742   EDITSTREAM es;
4743 
4744   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4745     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4746     FF_DONTCARE, "Courier");
4747 
4748   setText.codepage = 1200;  /* no constant for unicode */
4749   setText.flags = ST_KEEPUNDO;
4750 
4751 
4752   /* modify flag shouldn't be set when richedit is first created */
4753   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4754   ok (result == 0,
4755       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4756 
4757   /* setting modify flag should actually set it */
4758   SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4759   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4760   ok (result != 0,
4761       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4762 
4763   /* clearing modify flag should actually clear it */
4764   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4765   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4766   ok (result == 0,
4767       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4768 
4769   /* setting font doesn't change modify flag */
4770   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4771   SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4772   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4773   ok (result == 0,
4774       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4775 
4776   /* setting text should set modify flag */
4777   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4778   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4779   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4780   ok (result != 0,
4781       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4782 
4783   /* undo previous text doesn't reset modify flag */
4784   SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4785   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4786   ok (result != 0,
4787       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4788 
4789   /* set text with no flag to keep undo stack should not set modify flag */
4790   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4791   setText.flags = 0;
4792   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4793   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4794   ok (result == 0,
4795       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4796 
4797   /* WM_SETTEXT doesn't modify */
4798   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4799   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4800   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4801   ok (result == 0,
4802       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4803 
4804   /* clear the text */
4805   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4806   SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4807   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4808   ok (result == 0,
4809       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4810 
4811   /* replace text */
4812   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4813   SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4814   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4815   SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4816   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4817   ok (result != 0,
4818       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4819 
4820   /* copy/paste text 1 */
4821   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4822   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4823   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4824   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4825   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4826   ok (result != 0,
4827       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4828 
4829   /* copy/paste text 2 */
4830   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4831   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4832   SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4833   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4834   SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4835   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4836   ok (result != 0,
4837       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4838 
4839   /* press char */
4840   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4841   SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4842   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4843   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4844   ok (result != 0,
4845       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4846 
4847   /* press del */
4848   SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4849   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4850   SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4851   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4852   ok (result != 0,
4853       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4854 
4855   /* set char format */
4856   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4857   cf2.cbSize = sizeof(CHARFORMAT2A);
4858   SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4859   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4860   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4861   SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4862   result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4863   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4864   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4865   ok (result != 0,
4866       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4867 
4868   /* set para format */
4869   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4870   pf2.cbSize = sizeof(PARAFORMAT2);
4871   SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4872   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4873   pf2.wAlignment = PFA_RIGHT;
4874   SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4875   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4876   ok (result == 0,
4877       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4878 
4879   /* EM_STREAM */
4880   SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4881   es.dwCookie = (DWORD_PTR)&streamText;
4882   es.dwError = 0;
4883   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4884   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4885   result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4886   ok (result != 0,
4887       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4888 
4889   DestroyWindow(hwndRichEdit);
4890 }
4891 
4892 struct exsetsel_s {
4893   LONG min;
4894   LONG max;
4895   LRESULT expected_retval;
4896   int expected_getsel_start;
4897   int expected_getsel_end;
4898   BOOL todo;
4899 };
4900 
4901 static const struct exsetsel_s exsetsel_tests[] = {
4902   /* sanity tests */
4903   {5, 10, 10, 5, 10 },
4904   {15, 17, 17, 15, 17 },
4905   /* test cpMax > strlen() */
4906   {0, 100, 18, 0, 18 },
4907   /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4908   {-1, 1, 17, 17, 17 },
4909   /* test cpMin == cpMax */
4910   {5, 5, 5, 5, 5 },
4911   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4912   {-1, 0, 5, 5, 5 },
4913   {-1, 17, 5, 5, 5 },
4914   {-1, 18, 5, 5, 5 },
4915   /* test cpMin < 0 && cpMax < 0 */
4916   {-1, -1, 17, 17, 17 },
4917   {-4, -5, 17, 17, 17 },
4918   /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4919   {0, -1, 18, 0, 18 },
4920   {17, -5, 18, 17, 18 },
4921   {18, -3, 17, 17, 17 },
4922   /* test if cpMin > cpMax */
4923   {15, 19, 18, 15, 18 },
4924   {19, 15, 18, 15, 18 },
4925   /* cpMin == strlen() && cpMax > cpMin */
4926   {17, 18, 18, 17, 18 },
4927   {17, 50, 18, 17, 18 },
4928 };
4929 
check_EM_EXSETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)4930 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4931     CHARRANGE cr;
4932     LRESULT result;
4933     int start, end;
4934 
4935     cr.cpMin = setsel->min;
4936     cr.cpMax = setsel->max;
4937     result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4938 
4939     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4940 
4941     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4942 
4943     todo_wine_if (setsel->todo)
4944         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4945             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4946 }
4947 
test_EM_EXSETSEL(void)4948 static void test_EM_EXSETSEL(void)
4949 {
4950     HWND hwndRichEdit = new_richedit(NULL);
4951     int i;
4952     const int num_tests = ARRAY_SIZE(exsetsel_tests);
4953 
4954     /* sending some text to the window */
4955     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4956     /*                                                 01234567890123456*/
4957     /*                                                          10      */
4958 
4959     for (i = 0; i < num_tests; i++) {
4960         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4961     }
4962 
4963     if (!is_lang_japanese)
4964         skip("Skip multibyte character tests on non-Japanese platform\n");
4965     else
4966     {
4967         CHARRANGE cr;
4968         char bufA[MAX_BUF_LEN] = {0};
4969         LRESULT result;
4970 
4971         /* Test with multibyte character */
4972         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4973         /*                                                 012345     6  78901 */
4974         cr.cpMin = 4; cr.cpMax = 8;
4975         result =  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4976         ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4977         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4978         ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4979         SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4980         ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4981         ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4982     }
4983 
4984     DestroyWindow(hwndRichEdit);
4985 }
4986 
check_EM_SETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)4987 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4988     LRESULT result;
4989     int start, end;
4990 
4991     result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4992 
4993     ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4994 
4995     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4996 
4997     todo_wine_if (setsel->todo)
4998         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4999             id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
5000 }
5001 
test_EM_SETSEL(void)5002 static void test_EM_SETSEL(void)
5003 {
5004     char buffA[32] = {0};
5005     HWND hwndRichEdit = new_richedit(NULL);
5006     int i;
5007     const int num_tests = ARRAY_SIZE(exsetsel_tests);
5008 
5009     /* sending some text to the window */
5010     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5011     /*                                                 01234567890123456*/
5012     /*                                                          10      */
5013 
5014     for (i = 0; i < num_tests; i++) {
5015         check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
5016     }
5017 
5018     SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
5019     buffA[0] = 123;
5020     SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5021     ok(buffA[0] == 0, "selection text %s\n", buffA);
5022 
5023     if (!is_lang_japanese)
5024         skip("Skip multibyte character tests on non-Japanese platform\n");
5025     else
5026     {
5027         int sel_start, sel_end;
5028         LRESULT result;
5029 
5030         /* Test with multibyte character */
5031         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5032         /*                                                 012345     6  78901 */
5033         result =  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
5034         ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
5035         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
5036         ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5037         result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5038         ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
5039         ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
5040     }
5041 
5042     DestroyWindow(hwndRichEdit);
5043 }
5044 
test_EM_REPLACESEL(int redraw)5045 static void test_EM_REPLACESEL(int redraw)
5046 {
5047     HWND hwndRichEdit = new_richedit(NULL);
5048     char buffer[1024] = {0};
5049     int r;
5050     GETTEXTEX getText;
5051     CHARRANGE cr;
5052     CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
5053     CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
5054 
5055     /* sending some text to the window */
5056     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5057     /*                                                 01234567890123456*/
5058     /*                                                          10      */
5059 
5060     /* FIXME add more tests */
5061     SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
5062     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
5063     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
5064     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5065     r = strcmp(buffer, "testing");
5066     ok(0 == r, "expected %d, got %d\n", 0, r);
5067 
5068     DestroyWindow(hwndRichEdit);
5069 
5070     hwndRichEdit = new_richedit(NULL);
5071 
5072     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5073     SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5074 
5075     /* Test behavior with carriage returns and newlines */
5076     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5077     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5078     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5079     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5080     r = strcmp(buffer, "RichEdit1");
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") == 0,
5089       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5090 
5091     /* Test number of lines reported after EM_REPLACESEL */
5092     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5093     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5094 
5095     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5096     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5097     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5098     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5099     r = strcmp(buffer, "RichEdit1\r\n");
5100     ok(0 == r, "expected %d, got %d\n", 0, r);
5101     getText.cb = 1024;
5102     getText.codepage = CP_ACP;
5103     getText.flags = GT_DEFAULT;
5104     getText.lpDefaultChar = NULL;
5105     getText.lpUsedDefChar = NULL;
5106     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5107     ok(strcmp(buffer, "RichEdit1\r") == 0,
5108       "EM_GETTEXTEX returned incorrect string\n");
5109 
5110     /* Test number of lines reported after EM_REPLACESEL */
5111     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5112     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5113 
5114     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5115     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5116     ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5117 
5118     /* Test number of lines reported after EM_REPLACESEL */
5119     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5120     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5121 
5122     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5123     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5124     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5125     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5126 
5127     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5128     r = strcmp(buffer, "RichEdit1\r\n");
5129     ok(0 == r, "expected %d, got %d\n", 0, r);
5130     getText.cb = 1024;
5131     getText.codepage = CP_ACP;
5132     getText.flags = GT_DEFAULT;
5133     getText.lpDefaultChar = NULL;
5134     getText.lpUsedDefChar = NULL;
5135     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5136     ok(strcmp(buffer, "RichEdit1\r") == 0,
5137       "EM_GETTEXTEX returned incorrect string\n");
5138 
5139     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5140     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5141     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
5142     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
5143 
5144     /* The following tests show that richedit should handle the special \r\r\n
5145        sequence by turning it into a single space on insertion. However,
5146        EM_REPLACESEL on WinXP returns the number of characters in the original
5147        string.
5148      */
5149 
5150     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5151     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5152     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5153     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5154     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5155     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5156     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5157 
5158     /* Test the actual string */
5159     getText.cb = 1024;
5160     getText.codepage = CP_ACP;
5161     getText.flags = GT_DEFAULT;
5162     getText.lpDefaultChar = NULL;
5163     getText.lpUsedDefChar = NULL;
5164     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5165     ok(strcmp(buffer, "\r\r") == 0,
5166       "EM_GETTEXTEX returned incorrect string\n");
5167 
5168     /* Test number of lines reported after EM_REPLACESEL */
5169     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5170     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5171 
5172     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5173     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5174     ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5175     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5176     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5177     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
5178     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
5179 
5180     /* Test the actual string */
5181     getText.cb = 1024;
5182     getText.codepage = CP_ACP;
5183     getText.flags = GT_DEFAULT;
5184     getText.lpDefaultChar = NULL;
5185     getText.lpUsedDefChar = NULL;
5186     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5187     ok(strcmp(buffer, " ") == 0,
5188       "EM_GETTEXTEX returned incorrect string\n");
5189 
5190     /* Test number of lines reported after EM_REPLACESEL */
5191     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5192     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5193 
5194     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5195     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5196     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5197     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5198     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5199     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5200     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5201 
5202     /* Test the actual string */
5203     getText.cb = 1024;
5204     getText.codepage = CP_ACP;
5205     getText.flags = GT_DEFAULT;
5206     getText.lpDefaultChar = NULL;
5207     getText.lpUsedDefChar = NULL;
5208     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5209     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5210       "EM_GETTEXTEX returned incorrect string\n");
5211 
5212     /* Test number of lines reported after EM_REPLACESEL */
5213     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5214     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5215 
5216     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5217     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5218     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5219     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5220     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5221     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5222     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5223 
5224     /* Test the actual string */
5225     getText.cb = 1024;
5226     getText.codepage = CP_ACP;
5227     getText.flags = GT_DEFAULT;
5228     getText.lpDefaultChar = NULL;
5229     getText.lpUsedDefChar = NULL;
5230     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5231     ok(strcmp(buffer, " \r") == 0,
5232       "EM_GETTEXTEX returned incorrect string\n");
5233 
5234     /* Test number of lines reported after EM_REPLACESEL */
5235     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5236     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5237 
5238     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5239     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5240     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5241     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5242     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5243     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
5244     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
5245 
5246     /* Test the actual string */
5247     getText.cb = 1024;
5248     getText.codepage = CP_ACP;
5249     getText.flags = GT_DEFAULT;
5250     getText.lpDefaultChar = NULL;
5251     getText.lpUsedDefChar = NULL;
5252     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5253     ok(strcmp(buffer, " \r\r") == 0,
5254       "EM_GETTEXTEX returned incorrect string\n");
5255 
5256     /* Test number of lines reported after EM_REPLACESEL */
5257     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5258     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5259 
5260     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5261     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5262     ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5263     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5264     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5265     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
5266     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
5267 
5268     /* Test the actual string */
5269     getText.cb = 1024;
5270     getText.codepage = CP_ACP;
5271     getText.flags = GT_DEFAULT;
5272     getText.lpDefaultChar = NULL;
5273     getText.lpUsedDefChar = NULL;
5274     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5275     ok(strcmp(buffer, "\rX\r\r\r") == 0,
5276       "EM_GETTEXTEX returned incorrect string\n");
5277 
5278     /* Test number of lines reported after EM_REPLACESEL */
5279     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5280     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5281 
5282     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5283     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5284     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5285     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5286     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5287     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5288     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5289 
5290     /* Test the actual string */
5291     getText.cb = 1024;
5292     getText.codepage = CP_ACP;
5293     getText.flags = GT_DEFAULT;
5294     getText.lpDefaultChar = NULL;
5295     getText.lpUsedDefChar = NULL;
5296     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5297     ok(strcmp(buffer, "\r\r") == 0,
5298       "EM_GETTEXTEX returned incorrect string\n");
5299 
5300     /* Test number of lines reported after EM_REPLACESEL */
5301     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5302     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5303 
5304     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5305     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5306     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5307     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5308     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5309     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5310     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5311 
5312     /* Test the actual string */
5313     getText.cb = 1024;
5314     getText.codepage = CP_ACP;
5315     getText.flags = GT_DEFAULT;
5316     getText.lpDefaultChar = NULL;
5317     getText.lpUsedDefChar = NULL;
5318     SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5319     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5320       "EM_GETTEXTEX returned incorrect string\n");
5321 
5322     /* Test number of lines reported after EM_REPLACESEL */
5323     r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5324     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5325 
5326     /* Test with  multibyte character */
5327     if (!is_lang_japanese)
5328         skip("Skip multibyte character tests on non-Japanese platform\n");
5329     else
5330     {
5331         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5332         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5333         todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5334         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5335         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5336         ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5337         ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5338         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5339         ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5340         ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5341 
5342         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5343         r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5344         todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5345         r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5346         ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5347         todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5348         todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5349         r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5350         todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5351         todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5352     }
5353 
5354     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5355     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5356     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5357     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5358     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5359     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5360     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5361     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5362     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5363 
5364     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5365     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5366     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5367     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5368     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5369     todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5370     todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5371     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5372     todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5373 
5374     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5375     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5376     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5377     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5378     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5379     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5380     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5381     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5382     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5383     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5384 
5385     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5386     SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5387     todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5388     todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5389     r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5390     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5391     todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5392     todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5393     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5394     todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5395 
5396     if (!redraw)
5397         /* This is needed to avoid interfering with keybd_event calls
5398          * on other tests that simulate keyboard events. */
5399         SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5400 
5401     DestroyWindow(hwndRichEdit);
5402 
5403     /* Single-line richedit */
5404     hwndRichEdit = new_richedit_with_style(NULL, 0);
5405     r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"line1\r\nline2");
5406     ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5407     r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5408     ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5409     ok(!strcmp(buffer, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buffer);
5410     DestroyWindow(hwndRichEdit);
5411 }
5412 
5413 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5414  * to test the state of the modifiers (Ctrl/Alt/Shift).
5415  *
5416  * Therefore Ctrl-<key> keystrokes need to be simulated with
5417  * keybd_event or by using SetKeyboardState to set the modifiers
5418  * and SendMessage to simulate the keystrokes.
5419  */
send_ctrl_key(HWND hwnd,UINT key)5420 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5421 {
5422     LRESULT result;
5423     hold_key(VK_CONTROL);
5424     result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5425     release_key(VK_CONTROL);
5426     return result;
5427 }
5428 
test_WM_PASTE(void)5429 static void test_WM_PASTE(void)
5430 {
5431     int result;
5432     char buffer[1024] = {0};
5433     const char* text1 = "testing paste\r";
5434     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5435     const char* text1_after = "testing paste\r\n";
5436     const char* text2 = "testing paste\r\rtesting paste";
5437     const char* text2_after = "testing paste\r\n\r\ntesting paste";
5438     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5439     HWND hwndRichEdit = new_richedit(NULL);
5440 
5441     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5442     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5443 
5444     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5445     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5446     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5447     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5448     /* Pasted text should be visible at this step */
5449     result = strcmp(text1_step1, buffer);
5450     ok(result == 0,
5451         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5452 
5453     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5454     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5455     /* Text should be the same as before (except for \r -> \r\n conversion) */
5456     result = strcmp(text1_after, buffer);
5457     ok(result == 0,
5458         "test paste: strcmp = %i, text='%s'\n", result, buffer);
5459 
5460     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5461     SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5462     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
5463     SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5464     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
5465     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5466     /* Pasted text should be visible at this step */
5467     result = strcmp(text3, buffer);
5468     ok(result == 0,
5469         "test paste: strcmp = %i\n", result);
5470     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
5471     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5472     /* Text should be the same as before (except for \r -> \r\n conversion) */
5473     result = strcmp(text2_after, buffer);
5474     ok(result == 0,
5475         "test paste: strcmp = %i\n", result);
5476     send_ctrl_key(hwndRichEdit, 'Y');   /* Redo */
5477     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5478     /* Text should revert to post-paste state */
5479     result = strcmp(buffer,text3);
5480     ok(result == 0,
5481         "test paste: strcmp = %i\n", result);
5482 
5483     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5484     /* Send WM_CHAR to simulate Ctrl-V */
5485     SendMessageA(hwndRichEdit, WM_CHAR, 22,
5486                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5487     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5488     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5489     result = strcmp(buffer,"");
5490     ok(result == 0,
5491         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5492 
5493     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5494      * with SetKeyboard state. */
5495 
5496     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5497     /* Simulates paste (Ctrl-V) */
5498     hold_key(VK_CONTROL);
5499     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5500                 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5501     release_key(VK_CONTROL);
5502     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5503     result = strcmp(buffer,"paste");
5504     ok(result == 0,
5505         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5506 
5507     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5508     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5509     /* Simulates copy (Ctrl-C) */
5510     hold_key(VK_CONTROL);
5511     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5512                 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5513     release_key(VK_CONTROL);
5514     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5515     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5516     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5517     result = strcmp(buffer,"testing");
5518     ok(result == 0,
5519         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5520 
5521     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5522     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5523     /* Simulates select all (Ctrl-A) */
5524     hold_key(VK_CONTROL);
5525     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5526                 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5527     /* Simulates select cut (Ctrl-X) */
5528     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5529                 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5530     release_key(VK_CONTROL);
5531     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5532     result = strcmp(buffer,"");
5533     ok(result == 0,
5534         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5535     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5536     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5537     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5538     result = strcmp(buffer,"cut\r\n");
5539     ok(result == 0,
5540         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5541     /* Simulates undo (Ctrl-Z) */
5542     hold_key(VK_CONTROL);
5543     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5544                 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5545     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5546     result = strcmp(buffer,"");
5547     ok(result == 0,
5548         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5549     /* Simulates redo (Ctrl-Y) */
5550     SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5551                 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5552     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5553     result = strcmp(buffer,"cut\r\n");
5554     ok(result == 0,
5555         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5556     release_key(VK_CONTROL);
5557 
5558     /* Copy multiline text to clipboard for future use */
5559     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
5560     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5561     SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5562     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
5563 
5564     /* Paste into read-only control */
5565     result = SendMessageA(hwndRichEdit, EM_SETREADONLY, TRUE, 0);
5566     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5567     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5568     result = strcmp(buffer, text3);
5569     ok(result == 0,
5570         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5571 
5572     /* Cut from read-only control */
5573     SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5574     SendMessageA(hwndRichEdit, WM_CUT, 0, 0);
5575     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5576     result = strcmp(buffer, text3);
5577     ok(result == 0,
5578         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5579 
5580     /* FIXME: Wine doesn't flush Ole clipboard when window is destroyed so do it manually */
5581     OleFlushClipboard();
5582     DestroyWindow(hwndRichEdit);
5583 
5584     /* Paste multi-line text into single-line control */
5585     hwndRichEdit = new_richedit_with_style(NULL, 0);
5586     SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5587     SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5588     result = strcmp(buffer, "testing paste");
5589     ok(result == 0,
5590         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5591     DestroyWindow(hwndRichEdit);
5592 }
5593 
test_EM_FORMATRANGE(void)5594 static void test_EM_FORMATRANGE(void)
5595 {
5596   int r, i, tpp_x, tpp_y;
5597   HDC hdc;
5598   HWND hwndRichEdit = new_richedit(NULL);
5599   FORMATRANGE fr;
5600   BOOL skip_non_english;
5601   static const struct {
5602     const char *string; /* The string */
5603     int first;          /* First 'pagebreak', 0 for don't care */
5604     int second;         /* Second 'pagebreak', 0 for don't care */
5605   } fmtstrings[] = {
5606     {"WINE wine", 0, 0},
5607     {"WINE wineWine", 0, 0},
5608     {"WINE\r\nwine\r\nwine", 5, 10},
5609     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5610     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5611   };
5612 
5613   skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5614   if (skip_non_english)
5615     skip("Skipping some tests on non-English platform\n");
5616 
5617   hdc = GetDC(hwndRichEdit);
5618   ok(hdc != NULL, "Could not get HDC\n");
5619 
5620   /* Calculate the twips per pixel */
5621   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5622   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5623 
5624   /* Test the simple case where all the text fits in the page rect. */
5625   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5626   fr.hdc = fr.hdcTarget = hdc;
5627   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5628   fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5629   fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5630   fr.chrg.cpMin = 0;
5631   fr.chrg.cpMax = -1;
5632   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5633   todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5634 
5635   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5636   fr.rc.bottom = fr.rcPage.bottom;
5637   r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5638   todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5639 
5640   SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5641 
5642   for (i = 0; i < ARRAY_SIZE(fmtstrings); i++)
5643   {
5644     GETTEXTLENGTHEX gtl;
5645     SIZE stringsize;
5646     int len;
5647 
5648     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5649 
5650     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5651     gtl.codepage = CP_ACP;
5652     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5653 
5654     /* Get some size information for the string */
5655     GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5656 
5657     /* Define the box to be half the width needed and a bit larger than the height.
5658      * Changes to the width means we have at least 2 pages. Changes to the height
5659      * is done so we can check the changing of fr.rc.bottom.
5660      */
5661     fr.hdc = fr.hdcTarget = hdc;
5662     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5663     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5664     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5665 
5666     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5667     todo_wine {
5668     ok(r == len, "Expected %d, got %d\n", len, r);
5669     }
5670 
5671     /* We know that the page can't hold the full string. See how many characters
5672      * are on the first one
5673      */
5674     fr.chrg.cpMin = 0;
5675     fr.chrg.cpMax = -1;
5676     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5677     todo_wine {
5678     if (! skip_non_english)
5679       ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5680     }
5681     if (fmtstrings[i].first)
5682       todo_wine {
5683       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5684       }
5685     else
5686       ok(r < len, "Expected < %d, got %d\n", len, r);
5687 
5688     /* Do another page */
5689     fr.chrg.cpMin = r;
5690     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5691     if (fmtstrings[i].second)
5692       todo_wine {
5693       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5694       }
5695     else if (! skip_non_english)
5696       ok (r < len, "Expected < %d, got %d\n", len, r);
5697 
5698     /* There is at least on more page, but we don't care */
5699 
5700     r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5701     todo_wine {
5702     ok(r == len, "Expected %d, got %d\n", len, r);
5703     }
5704   }
5705 
5706   ReleaseDC(NULL, hdc);
5707   DestroyWindow(hwndRichEdit);
5708 }
5709 
5710 static int nCallbackCount = 0;
5711 
EditStreamCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5712 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5713 				 LONG cb, LONG* pcb)
5714 {
5715   const char text[] = {'t','e','s','t'};
5716 
5717   if (sizeof(text) <= cb)
5718   {
5719     if ((int)dwCookie != nCallbackCount)
5720     {
5721       *pcb = 0;
5722       return 0;
5723     }
5724 
5725     memcpy (pbBuff, text, sizeof(text));
5726     *pcb = sizeof(text);
5727 
5728     nCallbackCount++;
5729 
5730     return 0;
5731   }
5732   else
5733     return 1; /* indicates callback failed */
5734 }
5735 
test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5736 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5737                                          LPBYTE pbBuff,
5738                                          LONG cb,
5739                                          LONG *pcb)
5740 {
5741   const char** str = (const char**)dwCookie;
5742   int size = strlen(*str);
5743   *pcb = cb;
5744   if (*pcb > size) {
5745     *pcb = size;
5746   }
5747   if (*pcb > 0) {
5748     memcpy(pbBuff, *str, *pcb);
5749     *str += *pcb;
5750   }
5751   return 0;
5752 }
5753 
test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5754 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5755                                          LPBYTE pbBuff,
5756                                          LONG cb,
5757                                          LONG *pcb)
5758 {
5759     DWORD *phase = (DWORD *)dwCookie;
5760 
5761     if(*phase == 0){
5762         static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5763         *pcb = sizeof(first) - 1;
5764         memcpy(pbBuff, first, *pcb);
5765     }else if(*phase == 1){
5766         static const char second[] = "\x8f\xc3\x8b";
5767         *pcb = sizeof(second) - 1;
5768         memcpy(pbBuff, second, *pcb);
5769     }else
5770         *pcb = 0;
5771 
5772     ++*phase;
5773 
5774     return 0;
5775 }
5776 
test_EM_STREAMIN_null_bytes(DWORD_PTR cookie,BYTE * buf,LONG size,LONG * written)5777 static DWORD CALLBACK test_EM_STREAMIN_null_bytes(DWORD_PTR cookie, BYTE *buf, LONG size, LONG *written)
5778 {
5779     DWORD *phase = (DWORD *)cookie;
5780 
5781     if (*phase == 0)
5782     {
5783         static const char first[] = "{\\rtf1\\ansi{Th\0is";
5784         *written = sizeof(first);
5785         memcpy(buf, first, *written);
5786     }
5787     else if (*phase == 1)
5788     {
5789         static const char second[] = " is a test}}";
5790         *written = sizeof(second);
5791         memcpy(buf, second, *written);
5792     }
5793     else
5794         *written = 0;
5795 
5796     ++*phase;
5797 
5798     return 0;
5799 }
5800 
5801 struct StringWithLength {
5802     int length;
5803     char *buffer;
5804 };
5805 
5806 /* This callback is used to handled the null characters in a string. */
test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)5807 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5808                                                    LPBYTE pbBuff,
5809                                                    LONG cb,
5810                                                    LONG *pcb)
5811 {
5812     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5813     int size = str->length;
5814     *pcb = cb;
5815     if (*pcb > size) {
5816       *pcb = size;
5817     }
5818     if (*pcb > 0) {
5819       memcpy(pbBuff, str->buffer, *pcb);
5820       str->buffer += *pcb;
5821       str->length -= *pcb;
5822     }
5823     return 0;
5824 }
5825 
test_EM_STREAMIN(void)5826 static void test_EM_STREAMIN(void)
5827 {
5828   HWND hwndRichEdit = new_richedit(NULL);
5829   DWORD phase;
5830   LRESULT result;
5831   EDITSTREAM es;
5832   char buffer[1024] = {0}, tmp[16];
5833   CHARRANGE range;
5834   PARAFORMAT2 fmt;
5835 
5836   const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5837   const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5838   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5839   const char * ptr;
5840 
5841   const char * streamText1 =
5842   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5843   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5844   "}\r\n";
5845 
5846   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5847   const char * streamText2 =
5848     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5849     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5850     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5851     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5852     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5853     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5854     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5855 
5856   const char * streamText3 = "RichEdit1";
5857 
5858   const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5859 
5860   const char * streamText4 =
5861       "This text just needs to be long enough to cause run to be split onto "
5862       "two separate lines and make sure the null terminating character is "
5863       "handled properly.\0";
5864 
5865   const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5866 
5867   int length4 = strlen(streamText4) + 1;
5868   struct StringWithLength cookieForStream4 = {
5869       length4,
5870       (char *)streamText4,
5871   };
5872 
5873   const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5874   int length5 = ARRAY_SIZE(streamText5);
5875   struct StringWithLength cookieForStream5 = {
5876       sizeof(streamText5),
5877       (char *)streamText5,
5878   };
5879 
5880   /* Minimal test without \par at the end */
5881   es.dwCookie = (DWORD_PTR)&streamText0;
5882   es.dwError = 0;
5883   es.pfnCallback = test_EM_STREAMIN_esCallback;
5884   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5885   ok(result == 12, "got %ld, expected %d\n", result, 12);
5886 
5887   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5888   ok (result  == 12,
5889       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5890   result = strcmp (buffer,"TestSomeText");
5891   ok (result  == 0,
5892       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5893   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5894   /* Show that para fmts are ignored */
5895   range.cpMin = 2;
5896   range.cpMax = 2;
5897   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5898   memset(&fmt, 0xcc, sizeof(fmt));
5899   fmt.cbSize = sizeof(fmt);
5900   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5901   ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5902   ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5903   ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5904   ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5905 
5906   /* Native richedit 2.0 ignores last \par */
5907   ptr = streamText0a;
5908   es.dwCookie = (DWORD_PTR)&ptr;
5909   es.dwError = 0;
5910   es.pfnCallback = test_EM_STREAMIN_esCallback;
5911   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5912   ok(result == 12, "got %ld, expected %d\n", result, 12);
5913 
5914   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5915   ok (result  == 12,
5916       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5917   result = strcmp (buffer,"TestSomeText");
5918   ok (result  == 0,
5919       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5920   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5921   /* This time para fmts are processed */
5922   range.cpMin = 2;
5923   range.cpMax = 2;
5924   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5925   memset(&fmt, 0xcc, sizeof(fmt));
5926   fmt.cbSize = sizeof(fmt);
5927   result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5928   ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5929   ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5930   ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5931   ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5932 
5933   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5934   es.dwCookie = (DWORD_PTR)&streamText0b;
5935   es.dwError = 0;
5936   es.pfnCallback = test_EM_STREAMIN_esCallback;
5937   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5938   ok(result == 13, "got %ld, expected %d\n", result, 13);
5939 
5940   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5941   ok (result  == 14,
5942       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5943   result = strcmp (buffer,"TestSomeText\r\n");
5944   ok (result  == 0,
5945       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5946   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5947 
5948   /* Show that when using SFF_SELECTION the last \par is not ignored. */
5949   ptr = streamText0a;
5950   es.dwCookie = (DWORD_PTR)&ptr;
5951   es.dwError = 0;
5952   es.pfnCallback = test_EM_STREAMIN_esCallback;
5953   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5954   ok(result == 12, "got %ld, expected %d\n", result, 12);
5955 
5956   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5957   ok (result  == 12,
5958       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5959   result = strcmp (buffer,"TestSomeText");
5960   ok (result  == 0,
5961       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5962   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5963 
5964   range.cpMin = 0;
5965   range.cpMax = -1;
5966   result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5967   ok (result == 13, "got %ld\n", result);
5968 
5969   ptr = streamText0a;
5970   es.dwCookie = (DWORD_PTR)&ptr;
5971   es.dwError = 0;
5972   es.pfnCallback = test_EM_STREAMIN_esCallback;
5973 
5974   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5975   ok(result == 13, "got %ld, expected 13\n", result);
5976 
5977   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5978   ok (result  == 14,
5979       "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5980   result = strcmp (buffer,"TestSomeText\r\n");
5981   ok (result  == 0,
5982       "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5983   ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5984 
5985   es.dwCookie = (DWORD_PTR)&streamText1;
5986   es.dwError = 0;
5987   es.pfnCallback = test_EM_STREAMIN_esCallback;
5988   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5989   ok(result == 12, "got %ld, expected %d\n", result, 12);
5990 
5991   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5992   ok (result  == 12,
5993       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5994   result = strcmp (buffer,"TestSomeText");
5995   ok (result  == 0,
5996       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5997   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5998 
5999   es.dwCookie = (DWORD_PTR)&streamText2;
6000   es.dwError = 0;
6001   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6002   ok(result == 0, "got %ld, expected %d\n", result, 0);
6003 
6004   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6005   ok (result  == 0,
6006       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
6007   ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6008   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
6009 
6010   es.dwCookie = (DWORD_PTR)&streamText3;
6011   es.dwError = 0;
6012   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6013   ok(result == 0, "got %ld, expected %d\n", result, 0);
6014 
6015   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6016   ok (result  == 0,
6017       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
6018   ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
6019   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
6020 
6021   es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
6022   es.dwError = 0;
6023   es.pfnCallback = test_EM_STREAMIN_esCallback;
6024   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6025   ok(result == 18, "got %ld, expected %d\n", result, 18);
6026 
6027   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6028   ok(result  == 15,
6029       "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
6030   result = strcmp (buffer,"TestUTF8WithBOM");
6031   ok(result  == 0,
6032       "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
6033   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
6034 
6035   phase = 0;
6036   es.dwCookie = (DWORD_PTR)&phase;
6037   es.dwError = 0;
6038   es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
6039   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6040   ok(result == 8, "got %ld\n", result);
6041 
6042   WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
6043 
6044   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6045   ok(result  == 3,
6046       "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
6047   result = memcmp (buffer, tmp, 3);
6048   ok(result  == 0,
6049       "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
6050   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
6051 
6052   es.dwCookie = (DWORD_PTR)&cookieForStream4;
6053   es.dwError = 0;
6054   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6055   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6056   ok(result == length4, "got %ld, expected %d\n", result, length4);
6057 
6058   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6059   ok (result  == length4,
6060       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
6061   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
6062 
6063   es.dwCookie = (DWORD_PTR)&cookieForStream5;
6064   es.dwError = 0;
6065   es.pfnCallback = test_EM_STREAMIN_esCallback2;
6066   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
6067   ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
6068 
6069   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6070   ok (result  == length5,
6071       "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
6072   ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
6073 
6074   DestroyWindow(hwndRichEdit);
6075 
6076   /* Single-line richedit */
6077   hwndRichEdit = new_richedit_with_style(NULL, 0);
6078   ptr = "line1\r\nline2";
6079   es.dwCookie = (DWORD_PTR)&ptr;
6080   es.dwError = 0;
6081   es.pfnCallback = test_EM_STREAMIN_esCallback;
6082   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6083   ok(result == 12, "got %ld, expected %d\n", result, 12);
6084   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6085   ok (!strcmp(buffer, "line1"),
6086       "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6087 
6088   /* Test 0-bytes inside text */
6089   hwndRichEdit = new_richedit_with_style(NULL, 0);
6090   phase = 0;
6091   es.dwCookie = (DWORD_PTR)&phase;
6092   es.dwError = 0;
6093   es.pfnCallback = test_EM_STREAMIN_null_bytes;
6094   result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6095   ok(result == 16, "got %ld, expected %d\n", result, 16);
6096   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6097   ok (!strcmp(buffer, "Th is  is a test"), "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6098 }
6099 
test_EM_StreamIn_Undo(void)6100 static void test_EM_StreamIn_Undo(void)
6101 {
6102   /* The purpose of this test is to determine when a EM_StreamIn should be
6103    * undoable. This is important because WM_PASTE currently uses StreamIn and
6104    * pasting should always be undoable but streaming isn't always.
6105    *
6106    * cases to test:
6107    * StreamIn plain text without SFF_SELECTION.
6108    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
6109    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
6110    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
6111    * Feel free to add tests for other text modes or StreamIn things.
6112    */
6113 
6114 
6115   HWND hwndRichEdit = new_richedit(NULL);
6116   LRESULT result;
6117   EDITSTREAM es;
6118   char buffer[1024] = {0};
6119   const char randomtext[] = "Some text";
6120 
6121   es.pfnCallback = EditStreamCallback;
6122 
6123   /* StreamIn, no SFF_SELECTION */
6124   es.dwCookie = nCallbackCount;
6125   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6126   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6127   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6128   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6129   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6130   result = strcmp (buffer,"test");
6131   ok (result  == 0,
6132       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6133 
6134   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6135   ok (result == FALSE,
6136       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
6137 
6138   /* StreamIn, SFF_SELECTION, but nothing selected */
6139   es.dwCookie = nCallbackCount;
6140   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6141   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6142   SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6143   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6144   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6145   result = strcmp (buffer,"testSome text");
6146   ok (result  == 0,
6147       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6148 
6149   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6150   ok (result == TRUE,
6151      "EM_STREAMIN with SFF_SELECTION but no selection set "
6152       "should create an undo\n");
6153 
6154   /* StreamIn, SFF_SELECTION, with a selection */
6155   es.dwCookie = nCallbackCount;
6156   SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6157   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6158   SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6159   SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6160   SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6161   result = strcmp (buffer,"Sometesttext");
6162   ok (result  == 0,
6163       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6164 
6165   result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6166   ok (result == TRUE,
6167       "EM_STREAMIN with SFF_SELECTION and selection set "
6168       "should create an undo\n");
6169 
6170   DestroyWindow(hwndRichEdit);
6171 }
6172 
is_em_settextex_supported(HWND hwnd)6173 static BOOL is_em_settextex_supported(HWND hwnd)
6174 {
6175     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6176     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6177 }
6178 
test_unicode_conversions(void)6179 static void test_unicode_conversions(void)
6180 {
6181     static const WCHAR tW[] = {'t',0};
6182     static const WCHAR teW[] = {'t','e',0};
6183     static const WCHAR textW[] = {'t','e','s','t',0};
6184     static const char textA[] = "test";
6185     char bufA[64];
6186     WCHAR bufW[64];
6187     HWND hwnd;
6188     int em_settextex_supported, ret;
6189 
6190 #define set_textA(hwnd, wm_set_text, txt) \
6191     do { \
6192         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6193         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6194         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6195         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6196         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
6197     } while(0)
6198 #define expect_textA(hwnd, wm_get_text, txt) \
6199     do { \
6200         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6201         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6202         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6203         memset(bufA, 0xAA, sizeof(bufA)); \
6204         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6205         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
6206         ret = lstrcmpA(bufA, txt); \
6207         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6208     } while(0)
6209 
6210 #define set_textW(hwnd, wm_set_text, txt) \
6211     do { \
6212         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6213         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6214         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6215         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6216         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
6217     } while(0)
6218 #define expect_textW(hwnd, wm_get_text, txt) \
6219     do { \
6220         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6221         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6222         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6223         memset(bufW, 0xAA, sizeof(bufW)); \
6224         ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6225         ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
6226         ret = lstrcmpW(bufW, txt); \
6227         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6228     } while(0)
6229 #define expect_empty(hwnd, wm_get_text) \
6230     do { \
6231         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6232         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6233         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6234         memset(bufA, 0xAA, sizeof(bufA)); \
6235         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6236         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6237         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6238     } while(0)
6239 
6240     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6241                            0, 0, 200, 60, 0, 0, 0, 0);
6242     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6243 
6244     ret = IsWindowUnicode(hwnd);
6245     ok(ret, "RichEdit20W should be unicode under NT\n");
6246 
6247     /* EM_SETTEXTEX is supported starting from version 3.0 */
6248     em_settextex_supported = is_em_settextex_supported(hwnd);
6249     trace("EM_SETTEXTEX is %ssupported on this platform\n",
6250           em_settextex_supported ? "" : "NOT ");
6251 
6252     expect_empty(hwnd, WM_GETTEXT);
6253     expect_empty(hwnd, EM_GETTEXTEX);
6254 
6255     ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6256     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6257     expect_textA(hwnd, WM_GETTEXT, "t");
6258     expect_textA(hwnd, EM_GETTEXTEX, "t");
6259     expect_textW(hwnd, EM_GETTEXTEX, tW);
6260 
6261     ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6262     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6263     expect_textA(hwnd, WM_GETTEXT, "te");
6264     expect_textA(hwnd, EM_GETTEXTEX, "te");
6265     expect_textW(hwnd, EM_GETTEXTEX, teW);
6266 
6267     set_textA(hwnd, WM_SETTEXT, NULL);
6268     expect_empty(hwnd, WM_GETTEXT);
6269     expect_empty(hwnd, EM_GETTEXTEX);
6270 
6271     set_textA(hwnd, WM_SETTEXT, textA);
6272     expect_textA(hwnd, WM_GETTEXT, textA);
6273     expect_textA(hwnd, EM_GETTEXTEX, textA);
6274     expect_textW(hwnd, EM_GETTEXTEX, textW);
6275 
6276     if (em_settextex_supported)
6277     {
6278         set_textA(hwnd, EM_SETTEXTEX, textA);
6279         expect_textA(hwnd, WM_GETTEXT, textA);
6280         expect_textA(hwnd, EM_GETTEXTEX, textA);
6281         expect_textW(hwnd, EM_GETTEXTEX, textW);
6282     }
6283 
6284     set_textW(hwnd, WM_SETTEXT, textW);
6285     expect_textW(hwnd, WM_GETTEXT, textW);
6286     expect_textA(hwnd, WM_GETTEXT, textA);
6287     expect_textW(hwnd, EM_GETTEXTEX, textW);
6288     expect_textA(hwnd, EM_GETTEXTEX, textA);
6289 
6290     if (em_settextex_supported)
6291     {
6292         set_textW(hwnd, EM_SETTEXTEX, textW);
6293         expect_textW(hwnd, WM_GETTEXT, textW);
6294         expect_textA(hwnd, WM_GETTEXT, textA);
6295         expect_textW(hwnd, EM_GETTEXTEX, textW);
6296         expect_textA(hwnd, EM_GETTEXTEX, textA);
6297     }
6298     DestroyWindow(hwnd);
6299 
6300     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6301                            0, 0, 200, 60, 0, 0, 0, 0);
6302     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6303 
6304     ret = IsWindowUnicode(hwnd);
6305     ok(!ret, "RichEdit20A should NOT be unicode\n");
6306 
6307     set_textA(hwnd, WM_SETTEXT, textA);
6308     expect_textA(hwnd, WM_GETTEXT, textA);
6309     expect_textA(hwnd, EM_GETTEXTEX, textA);
6310     expect_textW(hwnd, EM_GETTEXTEX, textW);
6311 
6312     if (em_settextex_supported)
6313     {
6314         set_textA(hwnd, EM_SETTEXTEX, textA);
6315         expect_textA(hwnd, WM_GETTEXT, textA);
6316         expect_textA(hwnd, EM_GETTEXTEX, textA);
6317         expect_textW(hwnd, EM_GETTEXTEX, textW);
6318     }
6319 
6320         set_textW(hwnd, WM_SETTEXT, textW);
6321         expect_textW(hwnd, WM_GETTEXT, textW);
6322         expect_textA(hwnd, WM_GETTEXT, textA);
6323         expect_textW(hwnd, EM_GETTEXTEX, textW);
6324         expect_textA(hwnd, EM_GETTEXTEX, textA);
6325 
6326     if (em_settextex_supported)
6327     {
6328         set_textW(hwnd, EM_SETTEXTEX, textW);
6329         expect_textW(hwnd, WM_GETTEXT, textW);
6330         expect_textA(hwnd, WM_GETTEXT, textA);
6331         expect_textW(hwnd, EM_GETTEXTEX, textW);
6332         expect_textA(hwnd, EM_GETTEXTEX, textA);
6333     }
6334     DestroyWindow(hwnd);
6335 }
6336 
test_WM_CHAR(void)6337 static void test_WM_CHAR(void)
6338 {
6339     HWND hwnd;
6340     int ret;
6341     const char * char_list = "abc\rabc\r";
6342     const char * expected_content_single = "abcabc";
6343     const char * expected_content_multi = "abc\r\nabc\r\n";
6344     char buffer[64] = {0};
6345     const char * p;
6346 
6347     /* single-line control must IGNORE carriage returns */
6348     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6349                            0, 0, 200, 60, 0, 0, 0, 0);
6350     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6351 
6352     p = char_list;
6353     while (*p != '\0') {
6354         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6355         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6356         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6357         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6358         p++;
6359     }
6360 
6361     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6362     ret = strcmp(buffer, expected_content_single);
6363     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6364 
6365     DestroyWindow(hwnd);
6366 
6367     /* multi-line control inserts CR normally */
6368     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6369                            0, 0, 200, 60, 0, 0, 0, 0);
6370     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6371 
6372     p = char_list;
6373     while (*p != '\0') {
6374         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6375         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6376         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6377         SendMessageA(hwnd, WM_KEYUP, *p, 1);
6378         p++;
6379     }
6380 
6381     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6382     ret = strcmp(buffer, expected_content_multi);
6383     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6384 
6385     DestroyWindow(hwnd);
6386 }
6387 
test_EM_GETTEXTLENGTHEX(void)6388 static void test_EM_GETTEXTLENGTHEX(void)
6389 {
6390     HWND hwnd;
6391     GETTEXTLENGTHEX gtl;
6392     int ret;
6393     const char * base_string = "base string";
6394     const char * test_string = "a\nb\n\n\r\n";
6395     const char * test_string_after = "a";
6396     const char * test_string_2 = "a\rtest\rstring";
6397     char buffer[64] = {0};
6398 
6399     /* single line */
6400     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6401                            0, 0, 200, 60, 0, 0, 0, 0);
6402     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6403 
6404     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6405     gtl.codepage = CP_ACP;
6406     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6407     ok(ret == 0, "ret %d\n",ret);
6408 
6409     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6410     gtl.codepage = CP_ACP;
6411     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6412     ok(ret == 0, "ret %d\n",ret);
6413 
6414     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6415 
6416     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6417     gtl.codepage = CP_ACP;
6418     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6419     ok(ret == strlen(base_string), "ret %d\n",ret);
6420 
6421     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6422     gtl.codepage = CP_ACP;
6423     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6424     ok(ret == strlen(base_string), "ret %d\n",ret);
6425 
6426     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6427 
6428     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6429     gtl.codepage = CP_ACP;
6430     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6431     ok(ret == 1, "ret %d\n",ret);
6432 
6433     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6434     gtl.codepage = CP_ACP;
6435     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6436     ok(ret == 1, "ret %d\n",ret);
6437 
6438     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6439     ret = strcmp(buffer, test_string_after);
6440     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6441 
6442     DestroyWindow(hwnd);
6443 
6444     /* multi line */
6445     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6446                            0, 0, 200, 60, 0, 0, 0, 0);
6447     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6448 
6449     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6450     gtl.codepage = CP_ACP;
6451     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6452     ok(ret == 0, "ret %d\n",ret);
6453 
6454     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6455     gtl.codepage = CP_ACP;
6456     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6457     ok(ret == 0, "ret %d\n",ret);
6458 
6459     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6460 
6461     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6462     gtl.codepage = CP_ACP;
6463     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6464     ok(ret == strlen(base_string), "ret %d\n",ret);
6465 
6466     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6467     gtl.codepage = CP_ACP;
6468     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6469     ok(ret == strlen(base_string), "ret %d\n",ret);
6470 
6471     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6472 
6473     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6474     gtl.codepage = CP_ACP;
6475     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6476     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6477 
6478     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6479     gtl.codepage = CP_ACP;
6480     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6481     ok(ret == strlen(test_string_2), "ret %d\n",ret);
6482 
6483     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6484 
6485     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6486     gtl.codepage = CP_ACP;
6487     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6488     ok(ret == 10, "ret %d\n",ret);
6489 
6490     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6491     gtl.codepage = CP_ACP;
6492     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6493     ok(ret == 6, "ret %d\n",ret);
6494 
6495     /* Unicode/NUMCHARS/NUMBYTES */
6496     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6497 
6498     gtl.flags = GTL_DEFAULT;
6499     gtl.codepage = 1200;
6500     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6501     ok(ret == lstrlenA(test_string_2),
6502        "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6503 
6504     gtl.flags = GTL_NUMCHARS;
6505     gtl.codepage = 1200;
6506     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6507     ok(ret == lstrlenA(test_string_2),
6508        "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6509 
6510     gtl.flags = GTL_NUMBYTES;
6511     gtl.codepage = 1200;
6512     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6513     ok(ret == lstrlenA(test_string_2)*2,
6514        "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6515 
6516     gtl.flags = GTL_PRECISE;
6517     gtl.codepage = 1200;
6518     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6519     ok(ret == lstrlenA(test_string_2)*2,
6520        "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6521 
6522     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6523     gtl.codepage = 1200;
6524     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6525     ok(ret == lstrlenA(test_string_2),
6526        "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6527 
6528     gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6529     gtl.codepage = 1200;
6530     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6531     ok(ret == E_INVALIDARG,
6532        "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6533 
6534     DestroyWindow(hwnd);
6535 }
6536 
6537 
6538 /* globals that parent and child access when checking event masks & notifications */
6539 static HWND eventMaskEditHwnd = 0;
6540 static int queriedEventMask;
6541 static int watchForEventMask = 0;
6542 
6543 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6544 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6545 {
6546     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6547     {
6548       queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6549     }
6550     return DefWindowProcA(hwnd, message, wParam, lParam);
6551 }
6552 
6553 /* test event masks in combination with WM_COMMAND */
test_eventMask(void)6554 static void test_eventMask(void)
6555 {
6556     HWND parent;
6557     int ret, style;
6558     WNDCLASSA cls;
6559     const char text[] = "foo bar\n";
6560     int eventMask;
6561 
6562     /* register class to capture WM_COMMAND */
6563     cls.style = 0;
6564     cls.lpfnWndProc = ParentMsgCheckProcA;
6565     cls.cbClsExtra = 0;
6566     cls.cbWndExtra = 0;
6567     cls.hInstance = GetModuleHandleA(0);
6568     cls.hIcon = 0;
6569     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6570     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6571     cls.lpszMenuName = NULL;
6572     cls.lpszClassName = "EventMaskParentClass";
6573     if(!RegisterClassA(&cls)) assert(0);
6574 
6575     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6576                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6577     ok (parent != 0, "Failed to create parent window\n");
6578 
6579     eventMaskEditHwnd = new_richedit(parent);
6580     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6581 
6582     eventMask = ENM_CHANGE | ENM_UPDATE;
6583     ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6584     ok(ret == ENM_NONE, "wrong event mask\n");
6585     ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6586     ok(ret == eventMask, "failed to set event mask\n");
6587 
6588     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6589     queriedEventMask = 0;  /* initialize to something other than we expect */
6590     watchForEventMask = EN_CHANGE;
6591     ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6592     ok(ret == TRUE, "failed to set text\n");
6593     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6594        notification in response to WM_SETTEXT */
6595     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6596             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6597 
6598     /* check to see if EN_CHANGE is sent when redraw is turned off */
6599     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6600     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6601     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6602     /* redraw is disabled by making the window invisible. */
6603     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6604     queriedEventMask = 0;  /* initialize to something other than we expect */
6605     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6606     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6607             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6608     SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6609     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6610 
6611     /* check to see if EN_UPDATE is sent when the editor isn't visible */
6612     SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6613     style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6614     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6615     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6616     watchForEventMask = EN_UPDATE;
6617     queriedEventMask = 0;  /* initialize to something other than we expect */
6618     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6619     ok(queriedEventMask == 0,
6620             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6621     SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6622     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6623     queriedEventMask = 0;  /* initialize to something other than we expect */
6624     SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6625     ok(queriedEventMask == eventMask,
6626             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6627 
6628 
6629     DestroyWindow(parent);
6630 }
6631 
6632 static int received_WM_NOTIFY = 0;
6633 static int modify_at_WM_NOTIFY = 0;
6634 static BOOL filter_on_WM_NOTIFY = FALSE;
6635 static HWND hwndRichedit_WM_NOTIFY;
6636 
WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6637 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6638 {
6639     if(message == WM_NOTIFY)
6640     {
6641       received_WM_NOTIFY = 1;
6642       modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6643       if (filter_on_WM_NOTIFY) return TRUE;
6644     }
6645     return DefWindowProcA(hwnd, message, wParam, lParam);
6646 }
6647 
test_WM_NOTIFY(void)6648 static void test_WM_NOTIFY(void)
6649 {
6650     HWND parent;
6651     WNDCLASSA cls;
6652     CHARFORMAT2A cf2;
6653     int sel_start, sel_end;
6654 
6655     /* register class to capture WM_NOTIFY */
6656     cls.style = 0;
6657     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6658     cls.cbClsExtra = 0;
6659     cls.cbWndExtra = 0;
6660     cls.hInstance = GetModuleHandleA(0);
6661     cls.hIcon = 0;
6662     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6663     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6664     cls.lpszMenuName = NULL;
6665     cls.lpszClassName = "WM_NOTIFY_ParentClass";
6666     if(!RegisterClassA(&cls)) assert(0);
6667 
6668     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6669                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
6670     ok (parent != 0, "Failed to create parent window\n");
6671 
6672     hwndRichedit_WM_NOTIFY = new_richedit(parent);
6673     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6674 
6675     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6676 
6677     /* Notifications for selection change should only be sent when selection
6678        actually changes. EM_SETCHARFORMAT is one message that calls
6679        ME_CommitUndo, which should check whether message should be sent */
6680     received_WM_NOTIFY = 0;
6681     cf2.cbSize = sizeof(CHARFORMAT2A);
6682     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6683     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6684     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6685     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6686     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6687 
6688     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6689        already at 0. */
6690     received_WM_NOTIFY = 0;
6691     modify_at_WM_NOTIFY = 0;
6692     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6693     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6694     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6695 
6696     received_WM_NOTIFY = 0;
6697     modify_at_WM_NOTIFY = 0;
6698     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6699     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6700 
6701     received_WM_NOTIFY = 0;
6702     modify_at_WM_NOTIFY = 0;
6703     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6704     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6705     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6706 
6707     /* Test for WM_NOTIFY messages with redraw disabled. */
6708     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6709     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6710     received_WM_NOTIFY = 0;
6711     SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6712     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6713     SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6714 
6715     /* Test filtering key events. */
6716     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6717     SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6718     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6719     received_WM_NOTIFY = 0;
6720     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6721     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6722     ok(sel_start == 1 && sel_end == 1,
6723        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6724     filter_on_WM_NOTIFY = TRUE;
6725     received_WM_NOTIFY = 0;
6726     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6727     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6728     ok(sel_start == 1 && sel_end == 1,
6729        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6730 
6731     /* test with owner set to NULL */
6732     SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6733     SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6734     SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6735     ok(sel_start == 1 && sel_end == 1,
6736        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6737 
6738     DestroyWindow(hwndRichedit_WM_NOTIFY);
6739     DestroyWindow(parent);
6740 }
6741 
6742 static ENLINK enlink;
6743 #define CURSOR_CLIENT_X 5
6744 #define CURSOR_CLIENT_Y 5
6745 #define WP_PARENT 1
6746 #define WP_CHILD 2
6747 
EN_LINK_ParentMsgCheckProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)6748 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6749 {
6750     if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6751     {
6752         enlink = *(ENLINK*)lParam;
6753     }
6754     return DefWindowProcA(hwnd, message, wParam, lParam);
6755 }
6756 
link_notify_test(const char * desc,int i,HWND hwnd,HWND parent,UINT msg,WPARAM wParam,LPARAM lParam,BOOL notifies)6757 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6758                              UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6759 {
6760     ENLINK junk_enlink;
6761 
6762     switch (msg)
6763     {
6764     case WM_LBUTTONDBLCLK:
6765     case WM_LBUTTONDOWN:
6766     case WM_LBUTTONUP:
6767     case WM_MOUSEHOVER:
6768     case WM_MOUSEMOVE:
6769     case WM_MOUSEWHEEL:
6770     case WM_RBUTTONDBLCLK:
6771     case WM_RBUTTONDOWN:
6772     case WM_RBUTTONUP:
6773         lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6774         break;
6775     case WM_SETCURSOR:
6776         if (wParam == WP_PARENT)
6777             wParam = (WPARAM)parent;
6778         else if (wParam == WP_CHILD)
6779             wParam = (WPARAM)hwnd;
6780         break;
6781     }
6782 
6783     memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6784     enlink = junk_enlink;
6785 
6786     SendMessageA(hwnd, msg, wParam, lParam);
6787 
6788     if (notifies)
6789     {
6790         ok(enlink.nmhdr.hwndFrom == hwnd,
6791            "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6792         ok(enlink.nmhdr.idFrom == 0,
6793            "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6794         ok(enlink.msg == msg,
6795            "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6796         if (msg == WM_SETCURSOR)
6797         {
6798             ok(enlink.wParam == 0,
6799                "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6800         }
6801         else
6802         {
6803             ok(enlink.wParam == wParam,
6804                "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6805         }
6806         ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6807            "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6808            desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6809         ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6810            "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6811     }
6812     else
6813     {
6814         ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6815            "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6816     }
6817 }
6818 
test_EN_LINK(void)6819 static void test_EN_LINK(void)
6820 {
6821     HWND hwnd, parent;
6822     WNDCLASSA cls;
6823     CHARFORMAT2A cf2;
6824     POINT orig_cursor_pos;
6825     POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6826     int i;
6827 
6828     static const struct
6829     {
6830         UINT msg;
6831         WPARAM wParam;
6832         LPARAM lParam;
6833         BOOL notifies;
6834     }
6835     link_notify_tests[] =
6836     {
6837         /* hold down the left button and try some messages */
6838         { WM_LBUTTONDOWN,    0,          0,  TRUE  }, /* 0 */
6839         { EM_LINESCROLL,     0,          1,  FALSE },
6840         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6841         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6842         { WM_MOUSEHOVER,     0,          0,  FALSE },
6843         { WM_MOUSEMOVE,      0,          0,  FALSE },
6844         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6845         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6846         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6847         { WM_RBUTTONUP,      0,          0,  TRUE  },
6848         { WM_SETCURSOR,      0,          0,  FALSE },
6849         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6850         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6851         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6852         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6853         { WM_LBUTTONUP,      0,          0,  TRUE  },
6854         /* hold down the right button and try some messages */
6855         { WM_RBUTTONDOWN,    0,          0,  TRUE  }, /* 16 */
6856         { EM_LINESCROLL,     0,          1,  FALSE },
6857         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6858         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6859         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6860         { WM_LBUTTONUP,      0,          0,  TRUE  },
6861         { WM_MOUSEHOVER,     0,          0,  FALSE },
6862         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6863         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6864         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6865         { WM_SETCURSOR,      0,          0,  FALSE },
6866         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6867         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6868         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6869         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE },
6870         { WM_RBUTTONUP,      0,          0,  TRUE  },
6871         /* try the messages with both buttons released */
6872         { EM_LINESCROLL,     0,          1,  FALSE }, /* 32 */
6873         { EM_SCROLL,         SB_BOTTOM,  0,  FALSE },
6874         { WM_LBUTTONDBLCLK,  0,          0,  TRUE  },
6875         { WM_LBUTTONDOWN,    0,          0,  TRUE  },
6876         { WM_LBUTTONUP,      0,          0,  TRUE  },
6877         { WM_MOUSEHOVER,     0,          0,  FALSE },
6878         { WM_MOUSEMOVE,      0,          0,  TRUE  },
6879         { WM_MOUSEWHEEL,     0,          0,  FALSE },
6880         { WM_RBUTTONDBLCLK,  0,          0,  TRUE  },
6881         { WM_RBUTTONDOWN,    0,          0,  TRUE  },
6882         { WM_RBUTTONUP,      0,          0,  TRUE  },
6883         { WM_SETCURSOR,      0,          0,  FALSE },
6884         { WM_SETCURSOR,      WP_CHILD,   0,  TRUE  },
6885         { WM_SETCURSOR,      WP_CHILD,   1,  TRUE  },
6886         { WM_SETCURSOR,      WP_PARENT,  0,  FALSE },
6887         { WM_VSCROLL,        SB_BOTTOM,  0,  FALSE }
6888     };
6889 
6890     /* register class to capture WM_NOTIFY */
6891     cls.style = 0;
6892     cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6893     cls.cbClsExtra = 0;
6894     cls.cbWndExtra = 0;
6895     cls.hInstance = GetModuleHandleA(0);
6896     cls.hIcon = 0;
6897     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6898     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6899     cls.lpszMenuName = NULL;
6900     cls.lpszClassName = "EN_LINK_ParentClass";
6901     if(!RegisterClassA(&cls)) assert(0);
6902 
6903     parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6904                            0, 0, 200, 60, NULL, NULL, NULL, NULL);
6905     ok(parent != 0, "Failed to create parent window\n");
6906 
6907     hwnd = new_richedit(parent);
6908     ok(hwnd != 0, "Failed to create edit window\n");
6909 
6910     SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6911 
6912     cf2.cbSize = sizeof(CHARFORMAT2A);
6913     cf2.dwMask = CFM_LINK;
6914     cf2.dwEffects = CFE_LINK;
6915     SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6916     /* mixing letters and numbers causes runs to be split */
6917     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6918 
6919     GetCursorPos(&orig_cursor_pos);
6920     SetCursorPos(0, 0);
6921 
6922     for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
6923     {
6924         link_notify_test("cursor position simulated", i, hwnd, parent,
6925                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6926                          link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6927     }
6928 
6929     ClientToScreen(hwnd, &cursor_screen_pos);
6930     SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6931 
6932     for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
6933     {
6934         link_notify_test("cursor position set", i, hwnd, parent,
6935                          link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6936                          link_notify_tests[i].notifies);
6937     }
6938 
6939     SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6940     DestroyWindow(hwnd);
6941     DestroyWindow(parent);
6942 }
6943 
test_undo_coalescing(void)6944 static void test_undo_coalescing(void)
6945 {
6946     HWND hwnd;
6947     int result;
6948     char buffer[64] = {0};
6949 
6950     /* multi-line control inserts CR normally */
6951     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6952                            0, 0, 200, 60, 0, 0, 0, 0);
6953     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6954 
6955     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6956     ok (result == FALSE, "Can undo after window creation.\n");
6957     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6958     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6959     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6960     ok (result == FALSE, "Can redo after window creation.\n");
6961     result = SendMessageA(hwnd, EM_REDO, 0, 0);
6962     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6963 
6964     /* Test the effect of arrows keys during typing on undo transactions*/
6965     simulate_typing_characters(hwnd, "one two three");
6966     SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6967     SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6968     simulate_typing_characters(hwnd, " four five six");
6969 
6970     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6971     ok (result == FALSE, "Can redo before anything is undone.\n");
6972     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6973     ok (result == TRUE, "Cannot undo typed characters.\n");
6974     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6975     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6976     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6977     ok (result == TRUE, "Cannot redo after undo.\n");
6978     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6979     result = strcmp(buffer, "one two three");
6980     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6981 
6982     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6983     ok (result == TRUE, "Cannot undo typed characters.\n");
6984     result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6985     ok (result == TRUE, "Failed to undo typed characters.\n");
6986     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6987     result = strcmp(buffer, "");
6988     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6989 
6990     /* Test the effect of focus changes during typing on undo transactions*/
6991     simulate_typing_characters(hwnd, "one two three");
6992     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6993     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6994     SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6995     SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6996     simulate_typing_characters(hwnd, " four five six");
6997     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6998     ok (result == TRUE, "Failed to undo typed characters.\n");
6999     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7000     result = strcmp(buffer, "one two three");
7001     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7002 
7003     /* Test the effect of the back key during typing on undo transactions */
7004     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7005     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7006     ok (result == TRUE, "Failed to clear the text.\n");
7007     simulate_typing_characters(hwnd, "one two threa");
7008     result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7009     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
7010     SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
7011     SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
7012     simulate_typing_characters(hwnd, "e four five six");
7013     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7014     ok (result == TRUE, "Failed to undo typed characters.\n");
7015     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7016     result = strcmp(buffer, "");
7017     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7018 
7019     /* Test the effect of the delete key during typing on undo transactions */
7020     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7021     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
7022     ok(result == TRUE, "Failed to set the text.\n");
7023     SendMessageA(hwnd, EM_SETSEL, 1, 1);
7024     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7025     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7026     SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7027     SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7028     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7029     ok (result == TRUE, "Failed to undo typed characters.\n");
7030     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7031     result = strcmp(buffer, "acd");
7032     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
7033     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7034     ok (result == TRUE, "Failed to undo typed characters.\n");
7035     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7036     result = strcmp(buffer, "abcd");
7037     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
7038 
7039     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
7040     SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7041     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7042     ok (result == TRUE, "Failed to clear the text.\n");
7043     simulate_typing_characters(hwnd, "one two three");
7044     result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
7045     ok (result == 0, "expected %d but got %d\n", 0, result);
7046     simulate_typing_characters(hwnd, " four five six");
7047     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7048     ok (result == TRUE, "Failed to undo typed characters.\n");
7049     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7050     result = strcmp(buffer, "one two three");
7051     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7052     result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7053     ok (result == TRUE, "Failed to undo typed characters.\n");
7054     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7055     result = strcmp(buffer, "");
7056     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7057 
7058     DestroyWindow(hwnd);
7059 }
7060 
customWordBreakProc(WCHAR * text,int pos,int bytes,int code)7061 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
7062 {
7063     int length;
7064 
7065     /* MSDN lied, length is actually the number of bytes. */
7066     length = bytes / sizeof(WCHAR);
7067     switch(code)
7068     {
7069         case WB_ISDELIMITER:
7070             return text[pos] == 'X';
7071         case WB_LEFT:
7072         case WB_MOVEWORDLEFT:
7073             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7074                 return pos-1;
7075             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
7076         case WB_LEFTBREAK:
7077             pos--;
7078             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7079                 pos--;
7080             return pos;
7081         case WB_RIGHT:
7082         case WB_MOVEWORDRIGHT:
7083             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7084                 return pos+1;
7085             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
7086         case WB_RIGHTBREAK:
7087             pos++;
7088             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7089                 pos++;
7090             return pos;
7091         default:
7092             ok(FALSE, "Unexpected code %d\n", code);
7093             break;
7094     }
7095     return 0;
7096 }
7097 
test_word_movement(void)7098 static void test_word_movement(void)
7099 {
7100     HWND hwnd;
7101     int result;
7102     int sel_start, sel_end;
7103     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
7104 
7105     /* multi-line control inserts CR normally */
7106     hwnd = new_richedit(NULL);
7107 
7108     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
7109     ok (result == TRUE, "Failed to clear the text.\n");
7110     SendMessageA(hwnd, EM_SETSEL, 0, 0);
7111     /* |one two three */
7112 
7113     send_ctrl_key(hwnd, VK_RIGHT);
7114     /* one |two  three */
7115     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7116     ok(sel_start == sel_end, "Selection should be empty\n");
7117     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7118 
7119     send_ctrl_key(hwnd, VK_RIGHT);
7120     /* one two  |three */
7121     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7122     ok(sel_start == sel_end, "Selection should be empty\n");
7123     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7124 
7125     send_ctrl_key(hwnd, VK_LEFT);
7126     /* one |two  three */
7127     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7128     ok(sel_start == sel_end, "Selection should be empty\n");
7129     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7130 
7131     send_ctrl_key(hwnd, VK_LEFT);
7132     /* |one two  three */
7133     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7134     ok(sel_start == sel_end, "Selection should be empty\n");
7135     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
7136 
7137     SendMessageA(hwnd, EM_SETSEL, 8, 8);
7138     /* one two | three */
7139     send_ctrl_key(hwnd, VK_RIGHT);
7140     /* one two  |three */
7141     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7142     ok(sel_start == sel_end, "Selection should be empty\n");
7143     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7144 
7145     SendMessageA(hwnd, EM_SETSEL, 11, 11);
7146     /* one two  th|ree */
7147     send_ctrl_key(hwnd, VK_LEFT);
7148     /* one two  |three */
7149     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7150     ok(sel_start == sel_end, "Selection should be empty\n");
7151     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7152 
7153     /* Test with a custom word break procedure that uses X as the delimiter. */
7154     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
7155     ok (result == TRUE, "Failed to clear the text.\n");
7156     SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7157     /* |one twoXthree */
7158     send_ctrl_key(hwnd, VK_RIGHT);
7159     /* one twoX|three */
7160     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7161     ok(sel_start == sel_end, "Selection should be empty\n");
7162     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7163 
7164     DestroyWindow(hwnd);
7165 
7166     /* Make sure the behaviour is the same with a unicode richedit window,
7167      * and using unicode functions. */
7168 
7169     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7170                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7171                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7172 
7173     /* Test with a custom word break procedure that uses X as the delimiter. */
7174     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7175     ok (result == TRUE, "Failed to clear the text.\n");
7176     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7177     /* |one twoXthree */
7178     send_ctrl_key(hwnd, VK_RIGHT);
7179     /* one twoX|three */
7180     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7181     ok(sel_start == sel_end, "Selection should be empty\n");
7182     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7183 
7184     DestroyWindow(hwnd);
7185 }
7186 
test_EM_CHARFROMPOS(void)7187 static void test_EM_CHARFROMPOS(void)
7188 {
7189     HWND hwnd;
7190     int result;
7191     RECT rcClient;
7192     POINTL point;
7193     point.x = 0;
7194     point.y = 40;
7195 
7196     /* multi-line control inserts CR normally */
7197     hwnd = new_richedit(NULL);
7198     result = SendMessageA(hwnd, WM_SETTEXT, 0,
7199                           (LPARAM)"one two three four five six seven\reight");
7200     ok(result == 1, "Expected 1, got %d\n", result);
7201     GetClientRect(hwnd, &rcClient);
7202 
7203     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7204     ok(result == 34, "expected character index of 34 but got %d\n", result);
7205 
7206     /* Test with points outside the bounds of the richedit control. */
7207     point.x = -1;
7208     point.y = 40;
7209     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7210     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7211 
7212     point.x = 1000;
7213     point.y = 0;
7214     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7215     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
7216 
7217     point.x = 1000;
7218     point.y = 36;
7219     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7220     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7221 
7222     point.x = 1000;
7223     point.y = -1;
7224     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7225     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7226 
7227     point.x = 1000;
7228     point.y = rcClient.bottom + 1;
7229     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7230     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7231 
7232     point.x = 1000;
7233     point.y = rcClient.bottom;
7234     result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7235     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
7236 
7237     DestroyWindow(hwnd);
7238 }
7239 
test_word_wrap(void)7240 static void test_word_wrap(void)
7241 {
7242     HWND hwnd;
7243     POINTL point = {0, 60}; /* This point must be below the first line */
7244     const char *text = "Must be long enough to test line wrapping";
7245     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7246     int res, pos, lines;
7247 
7248     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7249      * when specified on window creation and set later. */
7250     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7251                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7252     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7253     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7254     ok(res, "WM_SETTEXT failed.\n");
7255     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7256     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7257     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7258     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7259 
7260     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
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     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7266                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7267     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7268 
7269     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7270     ok(res, "WM_SETTEXT failed.\n");
7271     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7272     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7273     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7274     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7275 
7276     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7277     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7278     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7279     DestroyWindow(hwnd);
7280 
7281     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7282                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7283     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7284     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7285     ok(res, "WM_SETTEXT failed.\n");
7286     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7287     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7288 
7289     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7290     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7291     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7292     DestroyWindow(hwnd);
7293 
7294     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7295                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7296                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7297     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7298     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7299     ok(res, "WM_SETTEXT failed.\n");
7300     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7301     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7302 
7303     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7304     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7305     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7306 
7307     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7308     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7309     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7310     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7311     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7312 
7313     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7314     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7315     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7316     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7317     DestroyWindow(hwnd);
7318 
7319     /* Test to see if wrapping happens with redraw disabled. */
7320     hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7321                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7322     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7323     SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7324     res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7325     ok(res, "EM_REPLACESEL failed.\n");
7326     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7327     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7328     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7329     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7330     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7331 
7332     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7333     DestroyWindow(hwnd);
7334 }
7335 
test_autoscroll(void)7336 static void test_autoscroll(void)
7337 {
7338     HWND hwnd = new_richedit(NULL);
7339     int lines, ret, redraw;
7340     POINT pt;
7341 
7342     for (redraw = 0; redraw <= 1; redraw++) {
7343         trace("testing with WM_SETREDRAW=%d\n", redraw);
7344         SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7345         SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7346         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7347         ok(lines == 8, "%d lines instead of 8\n", lines);
7348         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7349         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7350         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7351         ret = GetWindowLongA(hwnd, GWL_STYLE);
7352         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7353 
7354         SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7355         lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7356         ok(lines == 1, "%d lines instead of 1\n", lines);
7357         ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7358         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7359         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7360         ret = GetWindowLongA(hwnd, GWL_STYLE);
7361         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7362     }
7363 
7364     SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7365     DestroyWindow(hwnd);
7366 
7367     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7368      * auto vertical/horizontal scrolling options. */
7369     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7370                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7371                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7372     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7373     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7374     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7375     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7376     ret = GetWindowLongA(hwnd, GWL_STYLE);
7377     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7378     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7379     DestroyWindow(hwnd);
7380 
7381     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7382                           WS_POPUP|ES_MULTILINE,
7383                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7384     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7385     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7386     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7387     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7388     ret = GetWindowLongA(hwnd, GWL_STYLE);
7389     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7390     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7391     DestroyWindow(hwnd);
7392 }
7393 
7394 
test_format_rect(void)7395 static void test_format_rect(void)
7396 {
7397     HWND hwnd;
7398     RECT rc, expected, clientRect;
7399     int n;
7400     DWORD options;
7401 
7402     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7403                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7404                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7405     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7406 
7407     GetClientRect(hwnd, &clientRect);
7408 
7409     expected = clientRect;
7410     InflateRect(&expected, -1, 0);
7411     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7412     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7413        wine_dbgstr_rect(&expected));
7414 
7415     for (n = -3; n <= 3; n++)
7416     {
7417       rc = clientRect;
7418       InflateRect(&rc, -n, -n);
7419       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7420 
7421       expected = rc;
7422       expected.top = max(0, rc.top);
7423       expected.left = max(0, rc.left);
7424       expected.bottom = min(clientRect.bottom, rc.bottom);
7425       expected.right = min(clientRect.right, rc.right);
7426       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7427       ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7428          wine_dbgstr_rect(&expected));
7429     }
7430 
7431     rc = clientRect;
7432     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7433     expected = clientRect;
7434     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7435     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7436        wine_dbgstr_rect(&expected));
7437 
7438     /* Adding the selectionbar adds the selectionbar width to the left side. */
7439     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7440     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7441     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7442     expected.left += 8; /* selection bar width */
7443     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7444     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7445        wine_dbgstr_rect(&expected));
7446 
7447     rc = clientRect;
7448     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7449     expected = clientRect;
7450     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7451     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7452        wine_dbgstr_rect(&expected));
7453 
7454     /* Removing the selectionbar subtracts the selectionbar width from the left side,
7455      * even if the left side is already 0. */
7456     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7457     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7458     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7459     expected.left -= 8; /* selection bar width */
7460     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7461     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7462        wine_dbgstr_rect(&expected));
7463 
7464     /* Set the absolute value of the formatting rectangle. */
7465     rc = clientRect;
7466     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7467     expected = clientRect;
7468     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7469     ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7470        wine_dbgstr_rect(&expected));
7471 
7472     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7473      * LPARAM as being a relative offset when the WPARAM value is 1, but these
7474      * tests show that this isn't true. */
7475     rc.top = 15;
7476     rc.left = 15;
7477     rc.bottom = clientRect.bottom - 15;
7478     rc.right = clientRect.right - 15;
7479     expected = rc;
7480     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7481     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7482     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7483        wine_dbgstr_rect(&expected));
7484 
7485     /* For some reason it does not limit the values to the client rect with
7486      * a WPARAM value of 1. */
7487     rc.top = -15;
7488     rc.left = -15;
7489     rc.bottom = clientRect.bottom + 15;
7490     rc.right = clientRect.right + 15;
7491     expected = rc;
7492     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7493     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7494     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7495        wine_dbgstr_rect(&expected));
7496 
7497     /* Reset to default rect and check how the format rect adjusts to window
7498      * resize and how it copes with very small windows */
7499     SendMessageA(hwnd, EM_SETRECT, 0, 0);
7500 
7501     MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7502     GetClientRect(hwnd, &clientRect);
7503 
7504     expected = clientRect;
7505     InflateRect(&expected, -1, 0);
7506     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7507     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7508        wine_dbgstr_rect(&expected));
7509 
7510     MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7511     GetClientRect(hwnd, &clientRect);
7512 
7513     expected = clientRect;
7514     InflateRect(&expected, -1, 0);
7515     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7516     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7517        wine_dbgstr_rect(&expected));
7518 
7519     MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7520     GetClientRect(hwnd, &clientRect);
7521 
7522     expected = clientRect;
7523     InflateRect(&expected, -1, 0);
7524     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7525     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7526        wine_dbgstr_rect(&expected));
7527 
7528     DestroyWindow(hwnd);
7529 
7530     /* The extended window style affects the formatting rectangle. */
7531     hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7532                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7533                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7534     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7535 
7536     GetClientRect(hwnd, &clientRect);
7537 
7538     expected = clientRect;
7539     expected.top += 1;
7540     InflateRect(&expected, -1, 0);
7541     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7542     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7543        wine_dbgstr_rect(&expected));
7544 
7545     rc = clientRect;
7546     InflateRect(&rc, -5, -5);
7547     expected = rc;
7548     expected.top -= 1;
7549     InflateRect(&expected, 1, 0);
7550     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7551     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7552     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7553        wine_dbgstr_rect(&expected));
7554 
7555     DestroyWindow(hwnd);
7556 }
7557 
test_WM_GETDLGCODE(void)7558 static void test_WM_GETDLGCODE(void)
7559 {
7560     HWND hwnd;
7561     UINT res, expected;
7562     MSG msg;
7563 
7564     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7565 
7566     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7567                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7568                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7569     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7570     msg.hwnd = hwnd;
7571     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7572     expected = expected | DLGC_WANTMESSAGE;
7573     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7574        res, expected);
7575     DestroyWindow(hwnd);
7576 
7577     msg.message = WM_KEYDOWN;
7578     msg.wParam = VK_RETURN;
7579     msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7580     msg.pt.x = 0;
7581     msg.pt.y = 0;
7582     msg.time = GetTickCount();
7583 
7584     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7585                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7586                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7587     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7588     msg.hwnd = hwnd;
7589     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7590     expected = expected | DLGC_WANTMESSAGE;
7591     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7592        res, expected);
7593     DestroyWindow(hwnd);
7594 
7595     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7596                           ES_MULTILINE|WS_POPUP,
7597                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7598     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7599     msg.hwnd = hwnd;
7600     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7601     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7602     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7603        res, expected);
7604     DestroyWindow(hwnd);
7605 
7606     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7607                           ES_WANTRETURN|WS_POPUP,
7608                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7609     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7610     msg.hwnd = hwnd;
7611     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7612     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7613     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7614        res, expected);
7615     DestroyWindow(hwnd);
7616 
7617     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7618                           WS_POPUP,
7619                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7620     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7621     msg.hwnd = hwnd;
7622     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7623     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7624     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7625        res, expected);
7626     DestroyWindow(hwnd);
7627 
7628     msg.wParam = VK_TAB;
7629     msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7630 
7631     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7632                           ES_MULTILINE|WS_POPUP,
7633                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7634     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7635     msg.hwnd = hwnd;
7636     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7637     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7638     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7639        res, expected);
7640     DestroyWindow(hwnd);
7641 
7642     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7643                           WS_POPUP,
7644                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7645     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7646     msg.hwnd = hwnd;
7647     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7648     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7649     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7650        res, expected);
7651     DestroyWindow(hwnd);
7652 
7653     hold_key(VK_CONTROL);
7654 
7655     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7656                           ES_MULTILINE|WS_POPUP,
7657                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7658     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7659     msg.hwnd = hwnd;
7660     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7661     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7662     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7663        res, expected);
7664     DestroyWindow(hwnd);
7665 
7666     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7667                           WS_POPUP,
7668                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7669     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7670     msg.hwnd = hwnd;
7671     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7672     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7673     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7674        res, expected);
7675     DestroyWindow(hwnd);
7676 
7677     release_key(VK_CONTROL);
7678 
7679     msg.wParam = 'a';
7680     msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7681 
7682     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7683                           ES_MULTILINE|WS_POPUP,
7684                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7685     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7686     msg.hwnd = hwnd;
7687     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7688     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7689     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7690        res, expected);
7691     DestroyWindow(hwnd);
7692 
7693     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7694                           WS_POPUP,
7695                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7696     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7697     msg.hwnd = hwnd;
7698     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7699     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7700     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7701        res, expected);
7702     DestroyWindow(hwnd);
7703 
7704     msg.message = WM_CHAR;
7705 
7706     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7707                           ES_MULTILINE|WS_POPUP,
7708                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7709     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7710     msg.hwnd = hwnd;
7711     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7712     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7713     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7714        res, expected);
7715     DestroyWindow(hwnd);
7716 
7717     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7718                           WS_POPUP,
7719                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7720     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7721     msg.hwnd = hwnd;
7722     res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7723     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7724     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7725        res, expected);
7726     DestroyWindow(hwnd);
7727 
7728     hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7729                           WS_POPUP|ES_SAVESEL,
7730                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7731     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7732     res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7733     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7734     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7735        res, expected);
7736     DestroyWindow(hwnd);
7737 }
7738 
test_zoom(void)7739 static void test_zoom(void)
7740 {
7741     HWND hwnd;
7742     UINT ret;
7743     RECT rc;
7744     POINT pt;
7745     int numerator, denominator;
7746 
7747     hwnd = new_richedit(NULL);
7748     GetClientRect(hwnd, &rc);
7749     pt.x = (rc.right - rc.left) / 2;
7750     pt.y = (rc.bottom - rc.top) / 2;
7751     ClientToScreen(hwnd, &pt);
7752 
7753     /* Test initial zoom value */
7754     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7755     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7756     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7757     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7758 
7759     /* test scroll wheel */
7760     hold_key(VK_CONTROL);
7761     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7762                       MAKELPARAM(pt.x, pt.y));
7763     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7764     release_key(VK_CONTROL);
7765 
7766     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7767     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7768     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7769     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7770 
7771     /* Test how much the mouse wheel can zoom in and out. */
7772     ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7773     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7774 
7775     hold_key(VK_CONTROL);
7776     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7777                       MAKELPARAM(pt.x, pt.y));
7778     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7779     release_key(VK_CONTROL);
7780 
7781     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7782     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7783     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7784     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7785 
7786     ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7787     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7788 
7789     hold_key(VK_CONTROL);
7790     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7791                       MAKELPARAM(pt.x, pt.y));
7792     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7793     release_key(VK_CONTROL);
7794 
7795     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7796     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7797     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7798     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7799 
7800     ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7801     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7802 
7803     hold_key(VK_CONTROL);
7804     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7805                       MAKELPARAM(pt.x, pt.y));
7806     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7807     release_key(VK_CONTROL);
7808 
7809     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7810     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7811     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7812     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7813 
7814     ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7815     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7816 
7817     hold_key(VK_CONTROL);
7818     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7819                       MAKELPARAM(pt.x, pt.y));
7820     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7821     release_key(VK_CONTROL);
7822 
7823     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7824     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7825     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7826     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7827 
7828     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7829     ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7830     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7831 
7832     hold_key(VK_CONTROL);
7833     ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7834                       MAKELPARAM(pt.x, pt.y));
7835     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7836     release_key(VK_CONTROL);
7837 
7838     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7839     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7840     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7841     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7842 
7843     /* Test bounds checking on EM_SETZOOM */
7844     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7845     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7846 
7847     ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7848     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7849 
7850     ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7851     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7852 
7853     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7854     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7855     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7856     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7857 
7858     ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7859     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7860 
7861     /* See if negative numbers are accepted. */
7862     ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7863     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7864 
7865     /* See if negative numbers are accepted. */
7866     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7867     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7868 
7869     ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7870     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7871     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7872     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7873 
7874     /* Reset the zoom value */
7875     ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7876     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7877 
7878     DestroyWindow(hwnd);
7879 }
7880 
7881 struct dialog_mode_messages
7882 {
7883     int wm_getdefid, wm_close, wm_nextdlgctl;
7884 };
7885 
7886 static struct dialog_mode_messages dm_messages;
7887 
7888 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7889     ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7890     "got %d\n", wmclose, dm_messages.wm_close); \
7891     ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7892     "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7893     ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7894     "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7895 
dialog_mode_wnd_proc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)7896 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7897 {
7898     switch (iMsg)
7899     {
7900         case DM_GETDEFID:
7901             dm_messages.wm_getdefid++;
7902             return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7903         case WM_NEXTDLGCTL:
7904             dm_messages.wm_nextdlgctl++;
7905             break;
7906         case WM_CLOSE:
7907             dm_messages.wm_close++;
7908             break;
7909     }
7910 
7911     return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7912 }
7913 
test_dialogmode(void)7914 static void test_dialogmode(void)
7915 {
7916     HWND hwRichEdit, hwParent, hwButton;
7917     MSG msg= {0};
7918     int lcount, r;
7919     WNDCLASSA cls;
7920 
7921     cls.style = 0;
7922     cls.lpfnWndProc = dialog_mode_wnd_proc;
7923     cls.cbClsExtra = 0;
7924     cls.cbWndExtra = 0;
7925     cls.hInstance = GetModuleHandleA(0);
7926     cls.hIcon = 0;
7927     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7928     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7929     cls.lpszMenuName = NULL;
7930     cls.lpszClassName = "DialogModeParentClass";
7931     if(!RegisterClassA(&cls)) assert(0);
7932 
7933     hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7934       CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7935 
7936     /* Test richedit(ES_MULTILINE) */
7937 
7938     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7939 
7940     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7941     ok(0 == r, "expected 0, got %d\n", r);
7942     lcount = SendMessageA(hwRichEdit,  EM_GETLINECOUNT, 0, 0);
7943     ok(2 == lcount, "expected 2, got %d\n", lcount);
7944 
7945     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7946     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7947 
7948     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7949     ok(0 == r, "expected 0, got %d\n", r);
7950     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7951     ok(3 == lcount, "expected 3, got %d\n", lcount);
7952 
7953     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7954     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7955     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7956     ok(0 == r, "expected 0, got %d\n", r);
7957     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7958     ok(3 == lcount, "expected 3, got %d\n", lcount);
7959 
7960     DestroyWindow(hwRichEdit);
7961 
7962     /* Test standalone richedit(ES_MULTILINE) */
7963 
7964     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7965 
7966     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7967     ok(0 == r, "expected 0, got %d\n", r);
7968     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7969     ok(2 == lcount, "expected 2, got %d\n", lcount);
7970 
7971     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7972     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7973 
7974     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7975     ok(0 == r, "expected 0, got %d\n", r);
7976     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7977     ok(2 == lcount, "expected 2, got %d\n", lcount);
7978 
7979     DestroyWindow(hwRichEdit);
7980 
7981     /* Check  a destination for messages */
7982 
7983     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7984 
7985     SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7986     SetParent( hwRichEdit, NULL);
7987 
7988     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7989     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7990 
7991     memset(&dm_messages, 0, sizeof(dm_messages));
7992     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7993     ok(0 == r, "expected 0, got %d\n", r);
7994     test_dm_messages(0, 1, 0);
7995 
7996     memset(&dm_messages, 0, sizeof(dm_messages));
7997     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7998     ok(0 == r, "expected 0, got %d\n", r);
7999     test_dm_messages(0, 0, 1);
8000 
8001     DestroyWindow(hwRichEdit);
8002 
8003     /* Check messages from richedit(ES_MULTILINE) */
8004 
8005     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
8006 
8007     memset(&dm_messages, 0, sizeof(dm_messages));
8008     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8009     ok(0 == r, "expected 0, got %d\n", r);
8010     test_dm_messages(0, 0, 0);
8011 
8012     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8013     ok(2 == lcount, "expected 2, got %d\n", lcount);
8014 
8015     memset(&dm_messages, 0, sizeof(dm_messages));
8016     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8017     ok(0 == r, "expected 0, got %d\n", r);
8018     test_dm_messages(0, 0, 0);
8019 
8020     memset(&dm_messages, 0, sizeof(dm_messages));
8021     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8022     ok(0 == r, "expected 0, got %d\n", r);
8023     test_dm_messages(0, 0, 0);
8024 
8025     memset(&dm_messages, 0, sizeof(dm_messages));
8026     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8027     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8028     test_dm_messages(0, 0, 0);
8029 
8030     memset(&dm_messages, 0, sizeof(dm_messages));
8031     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8032     ok(0 == r, "expected 0, got %d\n", r);
8033     test_dm_messages(0, 1, 0);
8034 
8035     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8036     ok(2 == lcount, "expected 2, got %d\n", lcount);
8037 
8038     memset(&dm_messages, 0, sizeof(dm_messages));
8039     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8040     ok(0 == r, "expected 0, got %d\n", r);
8041     test_dm_messages(0, 0, 0);
8042 
8043     memset(&dm_messages, 0, sizeof(dm_messages));
8044     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8045     ok(0 == r, "expected 0, got %d\n", r);
8046     test_dm_messages(0, 0, 1);
8047 
8048     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8049         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8050     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8051 
8052     memset(&dm_messages, 0, sizeof(dm_messages));
8053     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8054     ok(0 == r, "expected 0, got %d\n", r);
8055     test_dm_messages(0, 1, 1);
8056 
8057     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8058     ok(2 == lcount, "expected 2, got %d\n", lcount);
8059 
8060     DestroyWindow(hwButton);
8061     DestroyWindow(hwRichEdit);
8062 
8063     /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
8064 
8065     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
8066 
8067     memset(&dm_messages, 0, sizeof(dm_messages));
8068     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8069     ok(0 == r, "expected 0, got %d\n", r);
8070     test_dm_messages(0, 0, 0);
8071 
8072     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8073     ok(2 == lcount, "expected 2, got %d\n", lcount);
8074 
8075     memset(&dm_messages, 0, sizeof(dm_messages));
8076     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8077     ok(0 == r, "expected 0, got %d\n", r);
8078     test_dm_messages(0, 0, 0);
8079 
8080     memset(&dm_messages, 0, sizeof(dm_messages));
8081     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8082     ok(0 == r, "expected 0, got %d\n", r);
8083     test_dm_messages(0, 0, 0);
8084 
8085     memset(&dm_messages, 0, sizeof(dm_messages));
8086     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8087     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8088     test_dm_messages(0, 0, 0);
8089 
8090     memset(&dm_messages, 0, sizeof(dm_messages));
8091     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8092     ok(0 == r, "expected 0, got %d\n", r);
8093     test_dm_messages(0, 0, 0);
8094 
8095     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8096     ok(3 == lcount, "expected 3, got %d\n", lcount);
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, 0, 0);
8116 
8117     lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8118     ok(4 == lcount, "expected 4, got %d\n", lcount);
8119 
8120     DestroyWindow(hwButton);
8121     DestroyWindow(hwRichEdit);
8122 
8123     /* Check messages from richedit(0) */
8124 
8125     hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
8126 
8127     memset(&dm_messages, 0, sizeof(dm_messages));
8128     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8129     ok(0 == r, "expected 0, got %d\n", r);
8130     test_dm_messages(0, 0, 0);
8131 
8132     memset(&dm_messages, 0, sizeof(dm_messages));
8133     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8134     ok(0 == r, "expected 0, got %d\n", r);
8135     test_dm_messages(0, 0, 0);
8136 
8137     memset(&dm_messages, 0, sizeof(dm_messages));
8138     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8139     ok(0 == r, "expected 0, got %d\n", r);
8140     test_dm_messages(0, 0, 0);
8141 
8142     memset(&dm_messages, 0, sizeof(dm_messages));
8143     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8144     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8145     test_dm_messages(0, 0, 0);
8146 
8147     memset(&dm_messages, 0, sizeof(dm_messages));
8148     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8149     ok(0 == r, "expected 0, got %d\n", r);
8150     test_dm_messages(0, 1, 0);
8151 
8152     memset(&dm_messages, 0, sizeof(dm_messages));
8153     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8154     ok(0 == r, "expected 0, got %d\n", r);
8155     test_dm_messages(0, 0, 0);
8156 
8157     memset(&dm_messages, 0, sizeof(dm_messages));
8158     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8159     ok(0 == r, "expected 0, got %d\n", r);
8160     test_dm_messages(0, 0, 1);
8161 
8162     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8163         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8164     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8165 
8166     memset(&dm_messages, 0, sizeof(dm_messages));
8167     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8168     ok(0 == r, "expected 0, got %d\n", r);
8169     test_dm_messages(0, 1, 1);
8170 
8171     DestroyWindow(hwRichEdit);
8172 
8173     /* Check messages from richedit(ES_WANTRETURN) */
8174 
8175     hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8176 
8177     memset(&dm_messages, 0, sizeof(dm_messages));
8178     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8179     ok(0 == r, "expected 0, got %d\n", r);
8180     test_dm_messages(0, 0, 0);
8181 
8182     memset(&dm_messages, 0, sizeof(dm_messages));
8183     r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8184     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8185     test_dm_messages(0, 0, 0);
8186 
8187     memset(&dm_messages, 0, sizeof(dm_messages));
8188     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8189     ok(0 == r, "expected 0, got %d\n", r);
8190     test_dm_messages(0, 0, 0);
8191 
8192     hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8193         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8194     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
8195 
8196     memset(&dm_messages, 0, sizeof(dm_messages));
8197     r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8198     ok(0 == r, "expected 0, got %d\n", r);
8199     test_dm_messages(0, 0, 0);
8200 
8201     DestroyWindow(hwRichEdit);
8202     DestroyWindow(hwParent);
8203 }
8204 
test_EM_FINDWORDBREAK_W(void)8205 static void test_EM_FINDWORDBREAK_W(void)
8206 {
8207     static const struct {
8208         WCHAR c;
8209         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8210     } delimiter_tests[] = {
8211         {0x0a,   FALSE},         /* newline */
8212         {0x0b,   FALSE},         /* vertical tab */
8213         {0x0c,   FALSE},         /* form feed */
8214         {0x0d,   FALSE},         /* carriage return */
8215         {0x20,   TRUE},          /* space */
8216         {0x61,   FALSE},         /* capital letter a */
8217         {0xa0,   FALSE},         /* no-break space */
8218         {0x2000, FALSE},         /* en quad */
8219         {0x3000, FALSE},         /* Ideographic space */
8220         {0x1100, FALSE},         /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8221         {0x11ff, FALSE},         /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8222         {0x115f, FALSE},         /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8223         {0xac00, FALSE},         /* Hangul character GA*/
8224         {0xd7af, FALSE},         /* End of Hangul character chart */
8225         {0xf020, TRUE},          /* MS private for CP_SYMBOL round trip?, see kb897872 */
8226         {0xff20, FALSE},         /* fullwidth commercial @ */
8227         {WCH_EMBEDDING, FALSE},  /* object replacement character*/
8228     };
8229     int i;
8230     HWND hwndRichEdit = new_richeditW(NULL);
8231     ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8232     for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8233     {
8234         WCHAR wbuf[2];
8235         int result;
8236 
8237         wbuf[0] = delimiter_tests[i].c;
8238         wbuf[1] = 0;
8239         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8240         result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8241         todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8242             ok(result == delimiter_tests[i].isdelimiter,
8243                 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8244                 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8245     }
8246     DestroyWindow(hwndRichEdit);
8247 }
8248 
test_EM_FINDWORDBREAK_A(void)8249 static void test_EM_FINDWORDBREAK_A(void)
8250 {
8251     static const struct {
8252         WCHAR c;
8253         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
8254     } delimiter_tests[] = {
8255         {0x0a,   FALSE},         /* newline */
8256         {0x0b,   FALSE},         /* vertical tab */
8257         {0x0c,   FALSE},         /* form feed */
8258         {0x0d,   FALSE},         /* carriage return */
8259         {0x20,   TRUE},          /* space */
8260         {0x61,   FALSE},         /* capital letter a */
8261     };
8262     int i;
8263     HWND hwndRichEdit = new_richedit(NULL);
8264 
8265     ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8266     for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8267     {
8268         int result;
8269         char buf[2];
8270         buf[0] = delimiter_tests[i].c;
8271         buf[1] = 0;
8272         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8273         result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8274         todo_wine_if (buf[0] == 0x20)
8275             ok(result == delimiter_tests[i].isdelimiter,
8276                "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8277                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8278     }
8279     DestroyWindow(hwndRichEdit);
8280 }
8281 
format_test_result(char * target,const char * src)8282 static void format_test_result(char *target, const char *src)
8283 {
8284     int i;
8285     for (i = 0; i < strlen(src); i++)
8286         sprintf(target + 2*i, "%02x", src[i] & 0xFF);
8287     target[2*i] = 0;
8288 }
8289 
8290 /*
8291  * This test attempts to show the effect of enter on a richedit
8292  * control v1.0 inserts CRLF whereas for higher versions it only
8293  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8294  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8295  * does for higher. The same test is cloned in riched32 and riched20.
8296  * Also shows the difference between WM_CHAR/WM_KEYDOWN in v1.0 and higher versions
8297  */
test_enter(void)8298 static void test_enter(void)
8299 {
8300     static const struct {
8301       const char *initialtext;
8302       const int   cursor;
8303       const char *expectedwmtext;
8304       const char *expectedemtext;
8305       const char *expectedemtextcrlf;
8306     } testenteritems[] = {
8307       { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8308       { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8309       { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8310       { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8311       { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8312     };
8313 
8314   char expectedbuf[1024];
8315   char resultbuf[1024];
8316   HWND hwndRichEdit = new_richedit(NULL);
8317   UINT i;
8318   char buf[1024] = {0};
8319   GETTEXTEX getText = {sizeof(buf)};
8320   LRESULT result;
8321   const char *expected;
8322 
8323   for (i = 0; i < ARRAY_SIZE(testenteritems); i++)
8324   {
8325     /* Set the text to the initial text */
8326     result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8327     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8328 
8329     /* Send Enter */
8330     SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8331     simulate_typing_characters(hwndRichEdit, "\r");
8332 
8333     /* 1. Retrieve with WM_GETTEXT */
8334     buf[0] = 0x00;
8335     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8336     expected = testenteritems[i].expectedwmtext;
8337 
8338     format_test_result(resultbuf, buf);
8339     format_test_result(expectedbuf, expected);
8340 
8341     result = strcmp(expected, buf);
8342     ok (result == 0,
8343         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8344         i, resultbuf, expectedbuf);
8345 
8346     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8347     getText.flags = GT_DEFAULT;
8348     getText.codepage = CP_ACP;
8349     buf[0] = 0x00;
8350     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8351     expected = testenteritems[i].expectedemtext;
8352 
8353     format_test_result(resultbuf, buf);
8354     format_test_result(expectedbuf, expected);
8355 
8356     result = strcmp(expected, buf);
8357     ok (result == 0,
8358         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8359         i, resultbuf, expectedbuf);
8360 
8361     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8362     getText.flags = GT_USECRLF;
8363     getText.codepage = CP_ACP;
8364     buf[0] = 0x00;
8365     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8366     expected = testenteritems[i].expectedemtextcrlf;
8367 
8368     format_test_result(resultbuf, buf);
8369     format_test_result(expectedbuf, expected);
8370 
8371     result = strcmp(expected, buf);
8372     ok (result == 0,
8373         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8374         i, resultbuf, expectedbuf);
8375   }
8376 
8377   /* Show that WM_CHAR is handled differently from WM_KEYDOWN */
8378   getText.flags    = GT_DEFAULT;
8379   getText.codepage = CP_ACP;
8380 
8381   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8382   ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8383   SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8384   SendMessageW(hwndRichEdit, WM_KEYDOWN, VK_RETURN, 0);
8385 
8386   result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8387   ok(result == 2, "Got %d\n", (int)result);
8388   format_test_result(resultbuf, buf);
8389   format_test_result(expectedbuf, "T\r");
8390   result = strcmp(resultbuf, expectedbuf);
8391   ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8392 
8393   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8394   ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
8395   SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8396   SendMessageW(hwndRichEdit, WM_CHAR, '\r', 0);
8397 
8398   result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8399   ok(result == 1, "Got %d\n", (int)result);
8400   format_test_result(resultbuf, buf);
8401   format_test_result(expectedbuf, "T");
8402   result = strcmp(resultbuf, expectedbuf);
8403   ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8404 
8405   DestroyWindow(hwndRichEdit);
8406 }
8407 
test_WM_CREATE(void)8408 static void test_WM_CREATE(void)
8409 {
8410     static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8411     static const char title[] = "line1\nline2";
8412 
8413     HWND rich_edit;
8414     LRESULT res;
8415     char buf[64];
8416     int len;
8417 
8418     rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8419             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8420     ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8421 
8422     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8423     ok(len == 5, "GetWindowText returned %d\n", len);
8424     ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8425 
8426     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8427     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8428 
8429     DestroyWindow(rich_edit);
8430 
8431     rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8432             0, 0, 200, 80, NULL, NULL, NULL, NULL);
8433     ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8434 
8435     len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8436     ok(len == 12, "GetWindowText returned %d\n", len);
8437     ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8438 
8439     res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8440     ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8441 
8442     DestroyWindow(rich_edit);
8443 }
8444 
8445 /*******************************************************************
8446  * Test that after deleting all of the text, the first paragraph
8447  * format reverts to the default.
8448  */
test_reset_default_para_fmt(void)8449 static void test_reset_default_para_fmt( void )
8450 {
8451     HWND richedit = new_richeditW( NULL );
8452     PARAFORMAT2 fmt;
8453     WORD def_align, new_align;
8454 
8455     memset( &fmt, 0, sizeof(fmt) );
8456     fmt.cbSize = sizeof(PARAFORMAT2);
8457     fmt.dwMask = -1;
8458     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8459     def_align = fmt.wAlignment;
8460     new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8461 
8462     simulate_typing_characters( richedit, "123" );
8463 
8464     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8465     fmt.dwMask = PFM_ALIGNMENT;
8466     fmt.wAlignment = new_align;
8467     SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8468 
8469     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8470     ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8471 
8472     SendMessageA( richedit, EM_SETSEL, 0, -1 );
8473     SendMessageA( richedit, WM_CUT, 0, 0 );
8474 
8475     SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8476     ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8477 
8478     DestroyWindow( richedit );
8479 }
8480 
test_EM_SETREADONLY(void)8481 static void test_EM_SETREADONLY(void)
8482 {
8483     HWND richedit = new_richeditW(NULL);
8484     DWORD dwStyle;
8485     LRESULT res;
8486 
8487     res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8488     ok(res == 1, "EM_SETREADONLY\n");
8489     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8490     ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8491 
8492     res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8493     ok(res == 1, "EM_SETREADONLY\n");
8494     dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8495     ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8496 
8497     DestroyWindow(richedit);
8498 }
8499 
twips2points(LONG value)8500 static inline LONG twips2points(LONG value)
8501 {
8502     return value / 20;
8503 }
8504 
8505 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8506     _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
_test_font_size(unsigned line,HWND hwnd,LONG size,LONG expected_size,LRESULT expected_res,BOOL expected_undo)8507 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8508                             LRESULT expected_res, BOOL expected_undo)
8509 {
8510     CHARFORMAT2A cf;
8511     LRESULT res;
8512     BOOL isundo;
8513 
8514     cf.cbSize = sizeof(cf);
8515     cf.dwMask = CFM_SIZE;
8516 
8517     res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8518     SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8519     isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8520     ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8521     ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8522                        twips2points(cf.yHeight), expected_size);
8523     ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8524                        isundo, expected_undo);
8525 }
8526 
test_EM_SETFONTSIZE(void)8527 static void test_EM_SETFONTSIZE(void)
8528 {
8529     HWND richedit = new_richedit(NULL);
8530     CHAR text[] = "wine";
8531     CHARFORMAT2A tmp_cf;
8532     LONG default_size;
8533 
8534     tmp_cf.cbSize = sizeof(tmp_cf);
8535     tmp_cf.dwMask = CFM_SIZE;
8536     tmp_cf.yHeight = 9 * 20.0;
8537     SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8538 
8539     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8540 
8541     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8542     /* without selection */
8543     TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8544     SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8545     default_size = twips2points(tmp_cf.yHeight);
8546     ok(default_size == 9, "Default font size should not be changed.\n");
8547     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8548 
8549     SendMessageA(richedit, EM_SETSEL, 0, 2);
8550 
8551     TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8552 
8553     SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8554     TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8555     ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8556 
8557     TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8558     TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8559     TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8560     TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8561     TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8562     TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8563     TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8564     TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8565     TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8566     TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8567     TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8568     TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8569     TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8570     TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8571     TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8572     TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8573     TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8574     TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8575     TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8576     TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8577     TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8578     TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8579     TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8580     TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8581     TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8582     TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8583     TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8584     TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8585 
8586     /* return FALSE when richedit is TM_PLAINTEXT mode */
8587     SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8588     SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8589     TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8590 
8591     DestroyWindow(richedit);
8592 }
8593 
test_alignment_style(void)8594 static void test_alignment_style(void)
8595 {
8596     HWND richedit = NULL;
8597     PARAFORMAT2 pf;
8598     DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8599                            ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8600                            ES_LEFT | ES_RIGHT | ES_CENTER};
8601     DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8602                           PFA_RIGHT, PFA_CENTER};
8603     const char * streamtext =
8604         "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8605         "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8606         "}\r\n";
8607     EDITSTREAM es;
8608     int i;
8609 
8610     for (i = 0; i < ARRAY_SIZE(align_style); i++)
8611     {
8612         DWORD dwStyle, new_align;
8613 
8614         richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8615         memset(&pf, 0, sizeof(pf));
8616         pf.cbSize = sizeof(PARAFORMAT2);
8617         pf.dwMask = -1;
8618 
8619         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8620         ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8621            i, pf.wAlignment, align_mask[i]);
8622         dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8623         ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8624            "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8625 
8626 
8627         /* Based on test_reset_default_para_fmt() */
8628         new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8629         simulate_typing_characters(richedit, "123");
8630 
8631         SendMessageW(richedit, EM_SETSEL, 0, -1);
8632         pf.dwMask = PFM_ALIGNMENT;
8633         pf.wAlignment = new_align;
8634         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8635 
8636         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8637         ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8638 
8639         SendMessageW(richedit, EM_SETSEL, 0, -1);
8640         SendMessageW(richedit, WM_CUT, 0, 0);
8641 
8642         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8643         ok(pf.wAlignment == align_mask[i], "got %d expect %d\n", pf.wAlignment, align_mask[i]);
8644 
8645         /* Test out of bounds tab count */
8646         pf.dwMask = PFM_TABSTOPS;
8647         pf.cTabCount = -25000;
8648         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8649         ok(pf.cTabCount == -25000, "Got %d\n", pf.cTabCount);
8650         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8651         ok(pf.cTabCount == 0, "Got %d\n", pf.cTabCount);
8652         pf.dwMask = PFM_TABSTOPS;
8653         pf.cTabCount = 25000;
8654         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8655         ok(pf.cTabCount == 25000, "Got %d\n", pf.cTabCount);
8656         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8657         ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8658         pf.dwMask = PFM_TABSTOPS;
8659         pf.cTabCount = 32;
8660         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8661         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8662         ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8663         pf.dwMask = PFM_TABSTOPS;
8664         pf.cTabCount = 33;
8665         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8666         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8667         ok(pf.cTabCount == 32, "Got %d\n", pf.cTabCount);
8668         pf.dwMask = PFM_TABSTOPS;
8669         pf.cTabCount = 1;
8670         SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8671         SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8672         ok(pf.cTabCount == 1, "Got %d\n", pf.cTabCount);
8673 
8674         DestroyWindow(richedit);
8675     }
8676 
8677     /* test with EM_STREAMIN */
8678     richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8679     simulate_typing_characters(richedit, "abc");
8680     es.dwCookie = (DWORD_PTR)&streamtext;
8681     es.dwError = 0;
8682     es.pfnCallback = test_EM_STREAMIN_esCallback;
8683     SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8684     SendMessageW(richedit, EM_SETSEL, 0, -1);
8685     memset(&pf, 0, sizeof(pf));
8686     pf.cbSize = sizeof(PARAFORMAT2);
8687     pf.dwMask = -1;
8688     SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8689     ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8690     DestroyWindow(richedit);
8691 }
8692 
test_WM_GETTEXTLENGTH(void)8693 static void test_WM_GETTEXTLENGTH(void)
8694 {
8695     HWND hwndRichEdit = new_richedit(NULL);
8696     static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8697     static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8698     static const char text3[] = "abcdef\x8e\xf0";
8699     int result;
8700 
8701     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8702     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8703     ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8704        result, lstrlenA(text1));
8705 
8706     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8707     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8708     ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8709        result, lstrlenA(text2));
8710 
8711     /* Test with multibyte character */
8712     if (!is_lang_japanese)
8713         skip("Skip multibyte character tests on non-Japanese platform\n");
8714     else
8715     {
8716         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8717         result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8718         todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8719     }
8720 
8721     DestroyWindow(hwndRichEdit);
8722 }
8723 
test_rtf(void)8724 static void test_rtf(void)
8725 {
8726     const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8727         "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8728     const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8729                                      0x201d,0x200e,0x200f,0x200d,0x200c};
8730     const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8731     const char *highlight = "{\\rtf1{\\colortbl;\\red0\\green0\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;}\\cf2\\highlight3 foo\\par}";
8732 
8733     HWND edit = new_richeditW( NULL );
8734     EDITSTREAM es;
8735     WCHAR buf[80];
8736     LRESULT result;
8737     PARAFORMAT2 fmt;
8738     CHARFORMAT2W cf;
8739 
8740     /* Test rtf specials */
8741     es.dwCookie = (DWORD_PTR)&specials;
8742     es.dwError = 0;
8743     es.pfnCallback = test_EM_STREAMIN_esCallback;
8744     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8745     ok( result == 11, "got %ld\n", result );
8746 
8747     result = SendMessageW( edit, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf );
8748     ok( result == ARRAY_SIZE(expect_specials), "got %ld\n", result );
8749     ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8750 
8751     /* Show that \rtlpar propagates to the second paragraph and is
8752        reset by \pard in the third. */
8753     es.dwCookie = (DWORD_PTR)&pard;
8754     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8755     ok( result == 11, "got %ld\n", result );
8756 
8757     fmt.cbSize = sizeof(fmt);
8758     SendMessageW( edit, EM_SETSEL, 1, 1 );
8759     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8760     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8761     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8762     SendMessageW( edit, EM_SETSEL, 5, 5 );
8763     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8764     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8765     ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8766     SendMessageW( edit, EM_SETSEL, 9, 9 );
8767     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8768     ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8769     ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8770 
8771     /* Test \highlight */
8772     es.dwCookie = (DWORD_PTR)&highlight;
8773     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8774     ok( result == 3, "got %ld\n", result );
8775     SendMessageW( edit, EM_SETSEL, 1, 1 );
8776     memset( &cf, 0, sizeof(cf) );
8777     cf.cbSize = sizeof(cf);
8778     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8779     ok( (cf.dwEffects & (CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR)) == 0, "got %08x\n", cf.dwEffects );
8780     ok( cf.crTextColor == RGB(128,128,128), "got %08x\n", cf.crTextColor );
8781     ok( cf.crBackColor == RGB(192,192,192), "got %08x\n", cf.crBackColor );
8782 
8783     DestroyWindow( edit );
8784 }
8785 
test_background(void)8786 static void test_background(void)
8787 {
8788     HWND hwndRichEdit = new_richedit(NULL);
8789 
8790     /* set the background color to black */
8791     ValidateRect(hwndRichEdit, NULL);
8792     SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8793     ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8794 
8795     DestroyWindow(hwndRichEdit);
8796 }
8797 
test_eop_char_fmt(void)8798 static void test_eop_char_fmt(void)
8799 {
8800     HWND edit = new_richedit( NULL );
8801     const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8802         "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8803         "\\f0\\fs25 Second\\par"
8804         "{\\f0\\fs26 Third}\\par"
8805         "{\\f0\\fs22 Fourth}\\par}}}";
8806     EDITSTREAM es;
8807     CHARFORMAT2W cf;
8808     int i, num, expect_height;
8809 
8810     es.dwCookie = (DWORD_PTR)&rtf;
8811     es.dwError = 0;
8812     es.pfnCallback = test_EM_STREAMIN_esCallback;
8813     num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8814     ok( num == 25, "got %d\n", num );
8815 
8816     for (i = 0; i <= num; i++)
8817     {
8818         SendMessageW( edit, EM_SETSEL, i, i + 1 );
8819         cf.cbSize = sizeof(cf);
8820         cf.dwMask = CFM_SIZE;
8821         SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8822         ok( cf.dwMask & CFM_SIZE, "%d: got %08x\n", i, cf.dwMask );
8823         if (i < 6) expect_height = 160;
8824         else if (i < 13) expect_height = 250;
8825         else if (i < 18) expect_height = 260;
8826         else if (i == 18 || i == 25) expect_height = 250;
8827         else expect_height = 220;
8828         ok( cf.yHeight == expect_height, "%d: got %d\n", i, cf.yHeight );
8829     }
8830 
8831     DestroyWindow( edit );
8832 }
8833 
test_para_numbering(void)8834 static void test_para_numbering(void)
8835 {
8836     HWND edit = new_richeditW( NULL );
8837     const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8838         "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8839         "\\fs20\\fi200\\li360\\f0 First\\par"
8840         "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
8841         "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
8842     const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
8843     EDITSTREAM es;
8844     WCHAR buf[80];
8845     LRESULT result;
8846     PARAFORMAT2 fmt, fmt2;
8847     GETTEXTEX get_text;
8848     CHARFORMAT2W cf;
8849 
8850     get_text.cb = sizeof(buf);
8851     get_text.flags = GT_RAWTEXT;
8852     get_text.codepage = 1200;
8853     get_text.lpDefaultChar = NULL;
8854     get_text.lpUsedDefChar = NULL;
8855 
8856     es.dwCookie = (DWORD_PTR)&numbers;
8857     es.dwError = 0;
8858     es.pfnCallback = test_EM_STREAMIN_esCallback;
8859     result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8860     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8861 
8862     result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
8863     ok( result == lstrlenW( expect_numbers_txt ), "got %ld\n", result );
8864     ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
8865 
8866     SendMessageW( edit, EM_SETSEL, 1, 1 );
8867     memset( &fmt, 0, sizeof(fmt) );
8868     fmt.cbSize = sizeof(fmt);
8869     fmt.dwMask = PFM_ALL2;
8870     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8871     ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
8872     ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
8873     ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
8874     ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
8875     ok( fmt.dxStartIndent == 560, "got %d\n", fmt.dxStartIndent );
8876     ok( fmt.dxOffset == -200, "got %d\n", fmt.dxOffset );
8877 
8878     /* Second para should have identical fmt */
8879     SendMessageW( edit, EM_SETSEL, 10, 10 );
8880     memset( &fmt2, 0, sizeof(fmt2) );
8881     fmt2.cbSize = sizeof(fmt2);
8882     fmt2.dwMask = PFM_ALL2;
8883     SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
8884     ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
8885 
8886     /* Check the eop heights - this determines the label height */
8887     SendMessageW( edit, EM_SETSEL, 12, 13 );
8888     cf.cbSize = sizeof(cf);
8889     cf.dwMask = CFM_SIZE;
8890     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8891     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8892 
8893     SendMessageW( edit, EM_SETSEL, 18, 19 );
8894     cf.cbSize = sizeof(cf);
8895     cf.dwMask = CFM_SIZE;
8896     SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8897     ok( cf.yHeight == 200, "got %d\n", cf.yHeight );
8898 
8899     DestroyWindow( edit );
8900 }
8901 
fill_reobject_struct(REOBJECT * reobj,LONG cp,LPOLEOBJECT poleobj,LPSTORAGE pstg,LPOLECLIENTSITE polesite,LONG sizel_cx,LONG sizel_cy,DWORD aspect,DWORD flags,DWORD user)8902 static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
8903                                  LPSTORAGE pstg, LPOLECLIENTSITE polesite, LONG sizel_cx,
8904                                  LONG sizel_cy, DWORD aspect, DWORD flags, DWORD user)
8905 {
8906     reobj->cbStruct = sizeof(*reobj);
8907     reobj->clsid = CLSID_NULL;
8908     reobj->cp = cp;
8909     reobj->poleobj = poleobj;
8910     reobj->pstg = pstg;
8911     reobj->polesite = polesite;
8912     reobj->sizel.cx = sizel_cx;
8913     reobj->sizel.cy = sizel_cy;
8914     reobj->dvaspect = aspect;
8915     reobj->dwFlags = flags;
8916     reobj->dwUser = user;
8917 }
8918 
test_EM_SELECTIONTYPE(void)8919 static void test_EM_SELECTIONTYPE(void)
8920 {
8921     HWND hwnd = new_richedit(NULL);
8922     IRichEditOle *reole = NULL;
8923     static const char text1[] = "abcdefg\n";
8924     int result;
8925     REOBJECT reo1, reo2;
8926     IOleClientSite *clientsite;
8927     HRESULT hr;
8928 
8929     SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text1);
8930     SendMessageA(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&reole);
8931 
8932     SendMessageA(hwnd, EM_SETSEL, 1, 1);
8933     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8934     ok(result == SEL_EMPTY, "got wrong selection type: %x.\n", result);
8935 
8936     SendMessageA(hwnd, EM_SETSEL, 1, 2);
8937     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8938     ok(result == SEL_TEXT, "got wrong selection type: %x.\n", result);
8939 
8940     SendMessageA(hwnd, EM_SETSEL, 2, 5);
8941     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8942     ok(result == (SEL_TEXT | SEL_MULTICHAR), "got wrong selection type: %x.\n", result);
8943 
8944     SendMessageA(hwnd, EM_SETSEL, 0, 1);
8945     hr = IRichEditOle_GetClientSite(reole, &clientsite);
8946     ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8947     fill_reobject_struct(&reo1, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8948                          DVASPECT_CONTENT, 0, 1);
8949     hr = IRichEditOle_InsertObject(reole, &reo1);
8950     ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8951     IOleClientSite_Release(clientsite);
8952 
8953     SendMessageA(hwnd, EM_SETSEL, 0, 1);
8954     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8955     ok(result == SEL_OBJECT, "got wrong selection type: %x.\n", result);
8956 
8957     SendMessageA(hwnd, EM_SETSEL, 0, 2);
8958     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8959     ok(result == (SEL_TEXT | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8960 
8961     SendMessageA(hwnd, EM_SETSEL, 0, 3);
8962     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8963     ok(result == (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT), "got wrong selection type: %x.\n", result);
8964 
8965     SendMessageA(hwnd, EM_SETSEL, 2, 3);
8966     hr = IRichEditOle_GetClientSite(reole, &clientsite);
8967     ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
8968     fill_reobject_struct(&reo2, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
8969                          DVASPECT_CONTENT, 0, 2);
8970     hr = IRichEditOle_InsertObject(reole, &reo2);
8971     ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
8972     IOleClientSite_Release(clientsite);
8973 
8974     SendMessageA(hwnd, EM_SETSEL, 0, 2);
8975     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8976     ok(result == (SEL_OBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8977 
8978     SendMessageA(hwnd, EM_SETSEL, 0, 3);
8979     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8980     ok(result == (SEL_OBJECT | SEL_MULTIOBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
8981 
8982     SendMessageA(hwnd, EM_SETSEL, 0, 4);
8983     result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
8984     ok(result == (SEL_TEXT| SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT), "got wrong selection type: %x.\n", result);
8985 
8986     IRichEditOle_Release(reole);
8987     DestroyWindow(hwnd);
8988 }
8989 
test_window_classes(void)8990 static void test_window_classes(void)
8991 {
8992     static const struct
8993     {
8994         const char *class;
8995         BOOL success;
8996     } test[] =
8997     {
8998         { "RichEdit", FALSE },
8999         { "RichEdit20A", TRUE },
9000         { "RichEdit20W", TRUE },
9001         { "RichEdit50A", FALSE },
9002         { "RichEdit50W", FALSE }
9003     };
9004     int i;
9005     HWND hwnd;
9006 
9007     for (i = 0; i < sizeof(test)/sizeof(test[0]); i++)
9008     {
9009         SetLastError(0xdeadbeef);
9010         hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
9011 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
9012         ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
9013            test[i].class, test[i].success ? "succeed" : "fail");
9014         if (!hwnd)
9015 todo_wine
9016             ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %d\n", GetLastError());
9017         else
9018             DestroyWindow(hwnd);
9019     }
9020 }
9021 
START_TEST(editor)9022 START_TEST( editor )
9023 {
9024   BOOL ret;
9025   /* Must explicitly LoadLibrary(). The test has no references to functions in
9026    * RICHED20.DLL, so the linker doesn't actually link to it. */
9027   hmoduleRichEdit = LoadLibraryA("riched20.dll");
9028   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
9029   is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
9030 
9031   test_window_classes();
9032   test_WM_CHAR();
9033   test_EM_FINDTEXT(FALSE);
9034   test_EM_FINDTEXT(TRUE);
9035   test_EM_GETLINE();
9036   test_EM_POSFROMCHAR();
9037   test_EM_SCROLLCARET();
9038   test_EM_SCROLL();
9039   test_scrollbar_visibility();
9040   test_WM_SETTEXT();
9041   test_EM_LINELENGTH();
9042   test_EM_SETCHARFORMAT();
9043   test_EM_SETTEXTMODE();
9044   test_TM_PLAINTEXT();
9045   test_EM_SETOPTIONS();
9046   test_WM_GETTEXT();
9047   test_EM_GETTEXTRANGE();
9048   test_EM_GETSELTEXT();
9049   test_EM_SETUNDOLIMIT();
9050   test_ES_PASSWORD();
9051   test_EM_SETTEXTEX();
9052   test_EM_LIMITTEXT();
9053   test_EM_EXLIMITTEXT();
9054   test_EM_GETLIMITTEXT();
9055   test_WM_SETFONT();
9056   test_EM_GETMODIFY();
9057   test_EM_SETSEL();
9058   test_EM_EXSETSEL();
9059   test_WM_PASTE();
9060   test_EM_STREAMIN();
9061   test_EM_STREAMOUT();
9062   test_EM_STREAMOUT_FONTTBL();
9063   test_EM_STREAMOUT_empty_para();
9064   test_EM_StreamIn_Undo();
9065   test_EM_FORMATRANGE();
9066   test_unicode_conversions();
9067   test_EM_GETTEXTLENGTHEX();
9068   test_WM_GETTEXTLENGTH();
9069   test_EM_REPLACESEL(1);
9070   test_EM_REPLACESEL(0);
9071   test_WM_NOTIFY();
9072   test_EN_LINK();
9073   test_EM_AUTOURLDETECT();
9074   test_eventMask();
9075   test_undo_coalescing();
9076   test_word_movement();
9077   test_EM_CHARFROMPOS();
9078   test_SETPARAFORMAT();
9079   test_word_wrap();
9080   test_autoscroll();
9081   test_format_rect();
9082   test_WM_GETDLGCODE();
9083   test_zoom();
9084   test_dialogmode();
9085   test_EM_FINDWORDBREAK_W();
9086   test_EM_FINDWORDBREAK_A();
9087   test_enter();
9088   test_WM_CREATE();
9089   test_reset_default_para_fmt();
9090   test_EM_SETREADONLY();
9091   test_EM_SETFONTSIZE();
9092   test_alignment_style();
9093   test_rtf();
9094   test_background();
9095   test_eop_char_fmt();
9096   test_para_numbering();
9097   test_EM_SELECTIONTYPE();
9098 
9099   /* Set the environment variable WINETEST_RICHED20 to keep windows
9100    * responsive and open for 30 seconds. This is useful for debugging.
9101    */
9102   if (getenv( "WINETEST_RICHED20" )) {
9103     keep_responsive(30);
9104   }
9105 
9106   OleFlushClipboard();
9107   ret = FreeLibrary(hmoduleRichEdit);
9108   ok(ret, "error: %d\n", (int) GetLastError());
9109 }
9110