Release 1.5.29.
[wine] / dlls / riched20 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
35
36 #define ID_RICHEDITTESTDBUTTON 0x123
37
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
39
40 #define ok_w3(format, szString1, szString2, szString3) \
41     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45        format, string1, string2, string3);
46
47 static HMODULE hmoduleRichEdit;
48
49 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
50   HWND hwnd;
51   hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53                       hmoduleRichEdit, NULL);
54   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
55   return hwnd;
56 }
57
58 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
59   HWND hwnd;
60   hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62                       hmoduleRichEdit, NULL);
63   ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
64   return hwnd;
65 }
66
67 static HWND new_richedit(HWND parent) {
68   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
69 }
70
71 static HWND new_richeditW(HWND parent) {
72   return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
73 }
74
75 /* Keeps the window reponsive for the deley_time in seconds.
76  * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
78 {
79     MSG msg;
80     time_t end;
81
82     /* The message pump uses PeekMessage() to empty the queue and then
83      * sleeps for 50ms before retrying the queue. */
84     end = time(NULL) + delay_time;
85     while (time(NULL) < end) {
86       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87         TranslateMessage(&msg);
88         DispatchMessage(&msg);
89       } else {
90         Sleep(50);
91       }
92     }
93 }
94
95 static void simulate_typing_characters(HWND hwnd, const char* szChars)
96 {
97     int ret;
98
99     while (*szChars != '\0') {
100         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
101         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
102         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
103         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
104         szChars++;
105     }
106 }
107
108 static BOOL hold_key(int vk)
109 {
110   BYTE key_state[256];
111   BOOL result;
112
113   result = GetKeyboardState(key_state);
114   ok(result, "GetKeyboardState failed.\n");
115   if (!result) return FALSE;
116   key_state[vk] |= 0x80;
117   result = SetKeyboardState(key_state);
118   ok(result, "SetKeyboardState failed.\n");
119   return result != 0;
120 }
121
122 static BOOL release_key(int vk)
123 {
124   BYTE key_state[256];
125   BOOL result;
126
127   result = GetKeyboardState(key_state);
128   ok(result, "GetKeyboardState failed.\n");
129   if (!result) return FALSE;
130   key_state[vk] &= ~0x80;
131   result = SetKeyboardState(key_state);
132   ok(result, "SetKeyboardState failed.\n");
133   return result != 0;
134 }
135
136 static const char haystack[] = "WINEWine wineWine wine WineWine";
137                              /* ^0        ^10       ^20       ^30 */
138
139 struct find_s {
140   int start;
141   int end;
142   const char *needle;
143   int flags;
144   int expected_loc;
145 };
146
147
148 static struct find_s find_tests[] = {
149   /* Find in empty text */
150   {0, -1, "foo", FR_DOWN, -1},
151   {0, -1, "foo", 0, -1},
152   {0, -1, "", FR_DOWN, -1},
153   {20, 5, "foo", FR_DOWN, -1},
154   {5, 20, "foo", FR_DOWN, -1}
155 };
156
157 static struct find_s find_tests2[] = {
158   /* No-result find */
159   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
160   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
161
162   /* Subsequent finds */
163   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
164   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
165   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
166   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
167
168   /* Find backwards */
169   {19, 20, "Wine", FR_MATCHCASE, 13},
170   {10, 20, "Wine", FR_MATCHCASE, 4},
171   {20, 10, "Wine", FR_MATCHCASE, 13},
172
173   /* Case-insensitive */
174   {1, 31, "wInE", FR_DOWN, 4},
175   {1, 31, "Wine", FR_DOWN, 4},
176
177   /* High-to-low ranges */
178   {20, 5, "Wine", FR_DOWN, -1},
179   {2, 1, "Wine", FR_DOWN, -1},
180   {30, 29, "Wine", FR_DOWN, -1},
181   {20, 5, "Wine", 0, 13},
182
183   /* Find nothing */
184   {5, 10, "", FR_DOWN, -1},
185   {10, 5, "", FR_DOWN, -1},
186   {0, -1, "", FR_DOWN, -1},
187   {10, 5, "", 0, -1},
188
189   /* Whole-word search */
190   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
191   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
192   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
194   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
195   {11, -1, "winewine", FR_WHOLEWORD, 0},
196   {31, -1, "winewine", FR_WHOLEWORD, 23},
197   
198   /* Bad ranges */
199   {5, 200, "XXX", FR_DOWN, -1},
200   {-20, 20, "Wine", FR_DOWN, -1},
201   {-20, 20, "Wine", FR_DOWN, -1},
202   {-15, -20, "Wine", FR_DOWN, -1},
203   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
204
205   /* Check the case noted in bug 4479 where matches at end aren't recognized */
206   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
207   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
208   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
210   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
211
212   /* The backwards case of bug 4479; bounds look right
213    * Fails because backward find is wrong */
214   {19, 20, "WINE", FR_MATCHCASE, 0},
215   {0, 20, "WINE", FR_MATCHCASE, -1},
216
217   {0, -1, "wineWine wine", 0, -1},
218 };
219
220 static WCHAR *atowstr(const char *str)
221 {
222     WCHAR *ret;
223     DWORD len;
224     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
225     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
226     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
227     return ret;
228 }
229
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
231 {
232   int findloc;
233
234   if(unicode){
235       FINDTEXTW ftw;
236       memset(&ftw, 0, sizeof(ftw));
237       ftw.chrg.cpMin = f->start;
238       ftw.chrg.cpMax = f->end;
239       ftw.lpstrText = atowstr(f->needle);
240
241       findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ftw);
242       ok(findloc == f->expected_loc,
243          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
244          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
245
246       findloc = SendMessage(hwnd, EM_FINDTEXTW, f->flags, (LPARAM) &ftw);
247       ok(findloc == f->expected_loc,
248          "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
249          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
250
251       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
252   }else{
253       FINDTEXTA fta;
254       memset(&fta, 0, sizeof(fta));
255       fta.chrg.cpMin = f->start;
256       fta.chrg.cpMax = f->end;
257       fta.lpstrText = f->needle;
258
259       findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &fta);
260       ok(findloc == f->expected_loc,
261          "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
262          name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
263   }
264 }
265
266 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
267     int id, BOOL unicode)
268 {
269   int findloc;
270   int expected_end_loc;
271
272   if(unicode){
273       FINDTEXTEXW ftw;
274       memset(&ftw, 0, sizeof(ftw));
275       ftw.chrg.cpMin = f->start;
276       ftw.chrg.cpMax = f->end;
277       ftw.lpstrText = atowstr(f->needle);
278       findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ftw);
279       ok(findloc == f->expected_loc,
280           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
281           name, id, f->needle, f->start, f->end, f->flags, findloc);
282       ok(ftw.chrgText.cpMin == f->expected_loc,
283           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
284           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
285       expected_end_loc = ((f->expected_loc == -1) ? -1
286             : f->expected_loc + strlen(f->needle));
287       ok(ftw.chrgText.cpMax == expected_end_loc,
288           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
289           name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
290       HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
291   }else{
292       FINDTEXTEXA fta;
293       memset(&fta, 0, sizeof(fta));
294       fta.chrg.cpMin = f->start;
295       fta.chrg.cpMax = f->end;
296       fta.lpstrText = f->needle;
297       findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &fta);
298       ok(findloc == f->expected_loc,
299           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
300           name, id, f->needle, f->start, f->end, f->flags, findloc);
301       ok(fta.chrgText.cpMin == f->expected_loc,
302           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
303           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
304       expected_end_loc = ((f->expected_loc == -1) ? -1
305             : f->expected_loc + strlen(f->needle));
306       ok(fta.chrgText.cpMax == expected_end_loc,
307           "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
308           name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
309   }
310 }
311
312 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
313     int num_tests, BOOL unicode)
314 {
315   int i;
316
317   for (i = 0; i < num_tests; i++) {
318       check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
319       check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
320   }
321 }
322
323 static void test_EM_FINDTEXT(BOOL unicode)
324 {
325   HWND hwndRichEdit;
326   CHARFORMAT2 cf2;
327
328   if(unicode)
329        hwndRichEdit = new_richeditW(NULL);
330   else
331        hwndRichEdit = new_richedit(NULL);
332
333   /* Empty rich edit control */
334   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
335       sizeof(find_tests)/sizeof(struct find_s), unicode);
336
337   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
338
339   /* Haystack text */
340   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
341       sizeof(find_tests2)/sizeof(struct find_s), unicode);
342
343   /* Setting a format on an arbitrary range should have no effect in search
344      results. This tests correct offset reporting across runs. */
345   cf2.cbSize = sizeof(CHARFORMAT2);
346   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
347   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
348   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
349   SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
350   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
351
352   /* Haystack text, again */
353   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
354       sizeof(find_tests2)/sizeof(struct find_s), unicode);
355
356   /* Yet another range */
357   cf2.dwMask = CFM_BOLD | cf2.dwMask;
358   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
359   SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
360   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
361
362   /* Haystack text, again */
363   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
364       sizeof(find_tests2)/sizeof(struct find_s), unicode);
365
366   DestroyWindow(hwndRichEdit);
367 }
368
369 static const struct getline_s {
370   int line;
371   size_t buffer_len;
372   const char *text;
373 } gl[] = {
374   {0, 10, "foo bar\r"},
375   {1, 10, "\r"},
376   {2, 10, "bar\r"},
377   {3, 10, "\r"},
378
379   /* Buffer smaller than line length */
380   {0, 2, "foo bar\r"},
381   {0, 1, "foo bar\r"},
382   {0, 0, "foo bar\r"}
383 };
384
385 static void test_EM_GETLINE(void)
386 {
387   int i;
388   HWND hwndRichEdit = new_richedit(NULL);
389   static const int nBuf = 1024;
390   char dest[1024], origdest[1024];
391   const char text[] = "foo bar\n"
392       "\n"
393       "bar\n";
394
395   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
396
397   memset(origdest, 0xBB, nBuf);
398   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
399   {
400     int nCopied;
401     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
402     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
403     memset(dest, 0xBB, nBuf);
404     *(WORD *) dest = gl[i].buffer_len;
405
406     /* EM_GETLINE appends a "\r\0" to the end of the line
407      * nCopied counts up to and including the '\r' */
408     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
409     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
410        expected_nCopied);
411     /* two special cases since a parameter is passed via dest */
412     if (gl[i].buffer_len == 0)
413       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
414          "buffer_len=0\n");
415     else if (gl[i].buffer_len == 1)
416       ok(dest[0] == gl[i].text[0] && !dest[1] &&
417          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
418     else
419     {
420       /* Prepare hex strings of buffers to dump on failure. */
421       char expectedbuf[1024];
422       char resultbuf[1024];
423       int j;
424       resultbuf[0] = '\0';
425       for (j = 0; j < 32; j++)
426         sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
427       expectedbuf[0] = '\0';
428       for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
429         sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
430       for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
431         sprintf(expectedbuf+strlen(expectedbuf), "??");
432       for (; j < 32; j++) /* Bytes after declared buffer size */
433         sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
434
435       /* Test the part of the buffer that is expected to be written according
436        * to the MSDN documentation fo EM_GETLINE, which does not state that
437        * a NULL terminating character will be added unless no text is copied.
438        *
439        * Windows NT does not append a NULL terminating character, but
440        * Windows 2000 and up do append a NULL terminating character if there
441        * is space in the buffer. The test will ignore this difference. */
442       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
443          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
444          i, expected_bytes_written, expectedbuf, resultbuf);
445       /* Test the part of the buffer after the declared length to make sure
446        * there are no buffer overruns. */
447       ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
448                   nBuf - gl[i].buffer_len),
449          "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
450          i, expected_bytes_written, expectedbuf, resultbuf);
451     }
452   }
453
454   DestroyWindow(hwndRichEdit);
455 }
456
457 static void test_EM_LINELENGTH(void)
458 {
459   HWND hwndRichEdit = new_richedit(NULL);
460   const char * text =
461         "richedit1\r"
462         "richedit1\n"
463         "richedit1\r\n"
464         "richedit1";
465   int offset_test[10][2] = {
466         {0, 9},
467         {5, 9},
468         {10, 9},
469         {15, 9},
470         {20, 9},
471         {25, 9},
472         {30, 9},
473         {35, 9},
474         {40, 0},
475         {45, 0},
476   };
477   int i;
478   LRESULT result;
479
480   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
481
482   for (i = 0; i < 10; i++) {
483     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
484     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
485         offset_test[i][0], result, offset_test[i][1]);
486   }
487
488   DestroyWindow(hwndRichEdit);
489 }
490
491 static int get_scroll_pos_y(HWND hwnd)
492 {
493   POINT p = {-1, -1};
494   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
495   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
496   return p.y;
497 }
498
499 static void move_cursor(HWND hwnd, LONG charindex)
500 {
501   CHARRANGE cr;
502   cr.cpMax = charindex;
503   cr.cpMin = charindex;
504   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
505 }
506
507 static void line_scroll(HWND hwnd, int amount)
508 {
509   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
510 }
511
512 static void test_EM_SCROLLCARET(void)
513 {
514   int prevY, curY;
515   const char text[] = "aa\n"
516       "this is a long line of text that should be longer than the "
517       "control's width\n"
518       "cc\n"
519       "dd\n"
520       "ee\n"
521       "ff\n"
522       "gg\n"
523       "hh\n";
524   /* The richedit window height needs to be large enough vertically to fit in
525    * more than two lines of text, so the new_richedit function can't be used
526    * since a height of 60 was not large enough on some systems.
527    */
528   HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
529                                    ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
530                                    0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
531   ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
532
533   /* Can't verify this */
534   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
535
536   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
537
538   /* Caret above visible window */
539   line_scroll(hwndRichEdit, 3);
540   prevY = get_scroll_pos_y(hwndRichEdit);
541   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
542   curY = get_scroll_pos_y(hwndRichEdit);
543   ok(prevY != curY, "%d == %d\n", prevY, curY);
544
545   /* Caret below visible window */
546   move_cursor(hwndRichEdit, sizeof(text) - 1);
547   line_scroll(hwndRichEdit, -3);
548   prevY = get_scroll_pos_y(hwndRichEdit);
549   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
550   curY = get_scroll_pos_y(hwndRichEdit);
551   ok(prevY != curY, "%d == %d\n", prevY, curY);
552
553   /* Caret in visible window */
554   move_cursor(hwndRichEdit, sizeof(text) - 2);
555   prevY = get_scroll_pos_y(hwndRichEdit);
556   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
557   curY = get_scroll_pos_y(hwndRichEdit);
558   ok(prevY == curY, "%d != %d\n", prevY, curY);
559
560   /* Caret still in visible window */
561   line_scroll(hwndRichEdit, -1);
562   prevY = get_scroll_pos_y(hwndRichEdit);
563   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
564   curY = get_scroll_pos_y(hwndRichEdit);
565   ok(prevY == curY, "%d != %d\n", prevY, curY);
566
567   DestroyWindow(hwndRichEdit);
568 }
569
570 static void test_EM_POSFROMCHAR(void)
571 {
572   HWND hwndRichEdit = new_richedit(NULL);
573   int i, expected;
574   LRESULT result;
575   unsigned int height = 0;
576   int xpos = 0;
577   POINTL pt;
578   LOCALESIGNATURE sig;
579   BOOL rtl;
580   static const char text[] = "aa\n"
581       "this is a long line of text that should be longer than the "
582       "control's width\n"
583       "cc\n"
584       "dd\n"
585       "ee\n"
586       "ff\n"
587       "gg\n"
588       "hh\n";
589
590   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
591                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
592          (sig.lsUsb[3] & 0x08000000) != 0);
593
594   /* Fill the control to lines to ensure that most of them are offscreen */
595   for (i = 0; i < 50; i++)
596   {
597     /* Do not modify the string; it is exactly 16 characters long. */
598     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
599     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
600   }
601
602   /*
603    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
604    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
605    Richedit 3.0 accepts either of the above API conventions.
606    */
607
608   /* Testing Richedit 2.0 API format */
609
610   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
611      Since all lines are identical and drawn with the same font,
612      they should have the same height... right?
613    */
614   for (i = 0; i < 50; i++)
615   {
616     /* All the lines are 16 characters long */
617     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
618     if (i == 0)
619     {
620       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
621       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
622       xpos = LOWORD(result);
623     }
624     else if (i == 1)
625     {
626       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
627       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
628       height = HIWORD(result);
629     }
630     else
631     {
632       ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
633       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
634     }
635   }
636
637   /* Testing position at end of text */
638   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
639   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
640   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
641
642   /* Testing position way past end of text */
643   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
644   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
645   expected = (rtl ? 8 : 1);
646   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
647
648   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
649   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
650   for (i = 0; i < 50; i++)
651   {
652     /* All the lines are 16 characters long */
653     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
654     ok((signed short)(HIWORD(result)) == (i - 1) * height,
655         "EM_POSFROMCHAR reports y=%hd, expected %d\n",
656         (signed short)(HIWORD(result)), (i - 1) * height);
657     ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
658   }
659
660   /* Testing position at end of text */
661   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
662   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
663   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
664
665   /* Testing position way past end of text */
666   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
667   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
668   expected = (rtl ? 8 : 1);
669   ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
670
671   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
672   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
673   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
674
675   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
676   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
677   ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
678   xpos = LOWORD(result);
679
680   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
681   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
682   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
683   ok((signed short)(LOWORD(result)) < xpos,
684         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
685         (signed short)(LOWORD(result)), xpos);
686   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
687
688   /* Test around end of text that doesn't end in a newline. */
689   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
690   SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
691               SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
692   ok(pt.x > 1, "pt.x = %d\n", pt.x);
693   xpos = pt.x;
694   SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
695               SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
696   ok(pt.x > xpos, "pt.x = %d\n", pt.x);
697   xpos = (rtl ? pt.x + 7 : pt.x);
698   SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
699               SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
700   ok(pt.x == xpos, "pt.x = %d\n", pt.x);
701
702   /* Try a negative position. */
703   SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
704   ok(pt.x == 1, "pt.x = %d\n", pt.x);
705
706   DestroyWindow(hwndRichEdit);
707 }
708
709 static void test_EM_SETCHARFORMAT(void)
710 {
711   HWND hwndRichEdit = new_richedit(NULL);
712   CHARFORMAT2 cf2;
713   int rc = 0;
714   int tested_effects[] = {
715     CFE_BOLD,
716     CFE_ITALIC,
717     CFE_UNDERLINE,
718     CFE_STRIKEOUT,
719     CFE_PROTECTED,
720     CFE_LINK,
721     CFE_SUBSCRIPT,
722     CFE_SUPERSCRIPT,
723     0
724   };
725   int i;
726   CHARRANGE cr;
727   LOCALESIGNATURE sig;
728   BOOL rtl;
729
730   rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
731                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
732          (sig.lsUsb[3] & 0x08000000) != 0);
733
734   /* Invalid flags, CHARFORMAT2 structure blanked out */
735   memset(&cf2, 0, sizeof(cf2));
736   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
737              (LPARAM) &cf2);
738   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
739
740   /* A valid flag, CHARFORMAT2 structure blanked out */
741   memset(&cf2, 0, sizeof(cf2));
742   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
743              (LPARAM) &cf2);
744   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
745
746   /* A valid flag, CHARFORMAT2 structure blanked out */
747   memset(&cf2, 0, sizeof(cf2));
748   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
749              (LPARAM) &cf2);
750   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
751
752   /* A valid flag, CHARFORMAT2 structure blanked out */
753   memset(&cf2, 0, sizeof(cf2));
754   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
755              (LPARAM) &cf2);
756   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
757
758   /* A valid flag, CHARFORMAT2 structure blanked out */
759   memset(&cf2, 0, sizeof(cf2));
760   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
761              (LPARAM) &cf2);
762   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
763
764   /* Invalid flags, CHARFORMAT2 structure minimally filled */
765   memset(&cf2, 0, sizeof(cf2));
766   cf2.cbSize = sizeof(CHARFORMAT2);
767   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
768              (LPARAM) &cf2);
769   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
770   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
771   ok(rc == FALSE, "Should not be able to undo here.\n");
772   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
773
774   /* A valid flag, CHARFORMAT2 structure minimally filled */
775   memset(&cf2, 0, sizeof(cf2));
776   cf2.cbSize = sizeof(CHARFORMAT2);
777   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
778              (LPARAM) &cf2);
779   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
780   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
781   ok(rc == FALSE, "Should not be able to undo here.\n");
782   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
783
784   /* A valid flag, CHARFORMAT2 structure minimally filled */
785   memset(&cf2, 0, sizeof(cf2));
786   cf2.cbSize = sizeof(CHARFORMAT2);
787   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
788              (LPARAM) &cf2);
789   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
790   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
791   ok(rc == FALSE, "Should not be able to undo here.\n");
792   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
793
794   /* A valid flag, CHARFORMAT2 structure minimally filled */
795   memset(&cf2, 0, sizeof(cf2));
796   cf2.cbSize = sizeof(CHARFORMAT2);
797   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
798              (LPARAM) &cf2);
799   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
800   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
801   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
802   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
803
804   /* A valid flag, CHARFORMAT2 structure minimally filled */
805   memset(&cf2, 0, sizeof(cf2));
806   cf2.cbSize = sizeof(CHARFORMAT2);
807   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
808              (LPARAM) &cf2);
809   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
810   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
811   ok(rc == TRUE, "Should not be able to undo here.\n");
812   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
813
814   cf2.cbSize = sizeof(CHARFORMAT2);
815   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
816              (LPARAM) &cf2);
817
818   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
819   cf2.cbSize = sizeof(CHARFORMAT2);
820   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
821              (LPARAM) &cf2);
822   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
823   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
824
825   /* wParam==0 is default char format, does not set modify */
826   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
827   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
828   ok(rc == 0, "Text marked as modified, expected not modified!\n");
829   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
830   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
831   if (! rtl)
832   {
833     rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
834     ok(rc == 0, "Text marked as modified, expected not modified!\n");
835   }
836   else
837     skip("RTL language found\n");
838
839   /* wParam==SCF_SELECTION sets modify if nonempty selection */
840   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
841   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
842   ok(rc == 0, "Text marked as modified, expected not modified!\n");
843   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
844   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
845   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
846   ok(rc == 0, "Text marked as modified, expected not modified!\n");
847
848   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
849   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
850   ok(rc == 0, "Text marked as modified, expected not modified!\n");
851   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
853   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
854   ok(rc == 0, "Text marked as modified, expected not modified!\n");
855   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
856   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
858   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
859   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
860
861   /* wParam==SCF_ALL sets modify regardless of whether text is present */
862   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
863   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
864   ok(rc == 0, "Text marked as modified, expected not modified!\n");
865   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
866   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
867   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
868   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
869
870   DestroyWindow(hwndRichEdit);
871
872   /* EM_GETCHARFORMAT tests */
873   for (i = 0; tested_effects[i]; i++)
874   {
875     hwndRichEdit = new_richedit(NULL);
876     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
877
878     /* Need to set a TrueType font to get consistent CFM_BOLD results */
879     memset(&cf2, 0, sizeof(CHARFORMAT2));
880     cf2.cbSize = sizeof(CHARFORMAT2);
881     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
882     cf2.dwEffects = 0;
883     strcpy(cf2.szFaceName, "Courier New");
884     cf2.wWeight = FW_DONTCARE;
885     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
886
887     memset(&cf2, 0, sizeof(CHARFORMAT2));
888     cf2.cbSize = sizeof(CHARFORMAT2);
889     SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
890     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
891     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
892           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
893           ||
894           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
895         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
896     ok((cf2.dwEffects & tested_effects[i]) == 0,
897         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
898
899     memset(&cf2, 0, sizeof(CHARFORMAT2));
900     cf2.cbSize = sizeof(CHARFORMAT2);
901     cf2.dwMask = tested_effects[i];
902     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
903       cf2.dwMask = CFM_SUPERSCRIPT;
904     cf2.dwEffects = tested_effects[i];
905     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
906     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
907
908     memset(&cf2, 0, sizeof(CHARFORMAT2));
909     cf2.cbSize = sizeof(CHARFORMAT2);
910     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
911     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
912     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
913           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
914           ||
915           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
916         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
917     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
918         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
919
920     memset(&cf2, 0, sizeof(CHARFORMAT2));
921     cf2.cbSize = sizeof(CHARFORMAT2);
922     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
923     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
924     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
926           ||
927           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
928         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
929     ok((cf2.dwEffects & tested_effects[i]) == 0,
930         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
931
932     memset(&cf2, 0, sizeof(CHARFORMAT2));
933     cf2.cbSize = sizeof(CHARFORMAT2);
934     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
935     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
937           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
938           ||
939           (cf2.dwMask & tested_effects[i]) == 0),
940         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
941
942     DestroyWindow(hwndRichEdit);
943   }
944
945   for (i = 0; tested_effects[i]; i++)
946   {
947     hwndRichEdit = new_richedit(NULL);
948     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
949
950     /* Need to set a TrueType font to get consistent CFM_BOLD results */
951     memset(&cf2, 0, sizeof(CHARFORMAT2));
952     cf2.cbSize = sizeof(CHARFORMAT2);
953     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
954     cf2.dwEffects = 0;
955     strcpy(cf2.szFaceName, "Courier New");
956     cf2.wWeight = FW_DONTCARE;
957     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
958
959     memset(&cf2, 0, sizeof(CHARFORMAT2));
960     cf2.cbSize = sizeof(CHARFORMAT2);
961     cf2.dwMask = tested_effects[i];
962     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
963       cf2.dwMask = CFM_SUPERSCRIPT;
964     cf2.dwEffects = tested_effects[i];
965     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
966     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
967
968     memset(&cf2, 0, sizeof(CHARFORMAT2));
969     cf2.cbSize = sizeof(CHARFORMAT2);
970     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
971     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
973           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
974           ||
975           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
976         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
977     ok((cf2.dwEffects & tested_effects[i]) == 0,
978         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
979
980     memset(&cf2, 0, sizeof(CHARFORMAT2));
981     cf2.cbSize = sizeof(CHARFORMAT2);
982     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
983     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
986           ||
987           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
988         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
989     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
990         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
991
992     memset(&cf2, 0, sizeof(CHARFORMAT2));
993     cf2.cbSize = sizeof(CHARFORMAT2);
994     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
995     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
996     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
997           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
998           ||
999           (cf2.dwMask & tested_effects[i]) == 0),
1000         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1001     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1002         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1003
1004     DestroyWindow(hwndRichEdit);
1005   }
1006
1007   /* Effects applied on an empty selection should take effect when selection is
1008      replaced with text */
1009   hwndRichEdit = new_richedit(NULL);
1010   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1011   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1012
1013   memset(&cf2, 0, sizeof(CHARFORMAT2));
1014   cf2.cbSize = sizeof(CHARFORMAT2);
1015   cf2.dwMask = CFM_BOLD;
1016   cf2.dwEffects = CFE_BOLD;
1017   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1018
1019   /* Selection is now nonempty */
1020   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1021
1022   memset(&cf2, 0, sizeof(CHARFORMAT2));
1023   cf2.cbSize = sizeof(CHARFORMAT2);
1024   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1025   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1026
1027   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1028       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1029   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1030       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1031
1032
1033   /* Set two effects on an empty selection */
1034   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1035   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1036
1037   memset(&cf2, 0, sizeof(CHARFORMAT2));
1038   cf2.cbSize = sizeof(CHARFORMAT2);
1039   cf2.dwMask = CFM_BOLD;
1040   cf2.dwEffects = CFE_BOLD;
1041   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042   cf2.dwMask = CFM_ITALIC;
1043   cf2.dwEffects = CFE_ITALIC;
1044   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1045
1046   /* Selection is now nonempty */
1047   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1048
1049   memset(&cf2, 0, sizeof(CHARFORMAT2));
1050   cf2.cbSize = sizeof(CHARFORMAT2);
1051   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1052   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1053
1054   ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1055       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1056   ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1057       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1058
1059   /* Setting the (empty) selection to exactly the same place as before should
1060      NOT clear the insertion style! */
1061   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1062   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1063
1064   memset(&cf2, 0, sizeof(CHARFORMAT2));
1065   cf2.cbSize = sizeof(CHARFORMAT2);
1066   cf2.dwMask = CFM_BOLD;
1067   cf2.dwEffects = CFE_BOLD;
1068   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1069
1070   /* Empty selection in same place, insert style should NOT be forgotten here. */
1071   SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1072
1073   /* Selection is now nonempty */
1074   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1075
1076   memset(&cf2, 0, sizeof(CHARFORMAT2));
1077   cf2.cbSize = sizeof(CHARFORMAT2);
1078   SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1079   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1080
1081   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1082       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1083   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1084       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1085
1086   /* Ditto with EM_EXSETSEL */
1087   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1088   cr.cpMin = 2; cr.cpMax = 2;
1089   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1090
1091   memset(&cf2, 0, sizeof(CHARFORMAT2));
1092   cf2.cbSize = sizeof(CHARFORMAT2);
1093   cf2.dwMask = CFM_BOLD;
1094   cf2.dwEffects = CFE_BOLD;
1095   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1096
1097   /* Empty selection in same place, insert style should NOT be forgotten here. */
1098   cr.cpMin = 2; cr.cpMax = 2;
1099   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1100
1101   /* Selection is now nonempty */
1102   SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1103
1104   memset(&cf2, 0, sizeof(CHARFORMAT2));
1105   cf2.cbSize = sizeof(CHARFORMAT2);
1106   cr.cpMin = 2; cr.cpMax = 6;
1107   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1108   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1109
1110   ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1111       "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1112   ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1113       "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1114
1115   DestroyWindow(hwndRichEdit);
1116 }
1117
1118 static void test_EM_SETTEXTMODE(void)
1119 {
1120   HWND hwndRichEdit = new_richedit(NULL);
1121   CHARFORMAT2 cf2, cf2test;
1122   CHARRANGE cr;
1123   int rc = 0;
1124
1125   /*Attempt to use mutually exclusive modes*/
1126   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1127   ok(rc == E_INVALIDARG,
1128      "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1129
1130   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1131   /*Insert text into the control*/
1132
1133   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1134
1135   /*Attempt to change the control to plain text mode*/
1136   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1137   ok(rc == E_UNEXPECTED,
1138      "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1139
1140   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1141   If rich text is pasted, it should have the same formatting as the rest
1142   of the text in the control*/
1143
1144   /*Italicize the text
1145   *NOTE: If the default text was already italicized, the test will simply
1146   reverse; in other words, it will copy a regular "wine" into a plain
1147   text window that uses an italicized format*/
1148   cf2.cbSize = sizeof(CHARFORMAT2);
1149   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1150              (LPARAM) &cf2);
1151
1152   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1153   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1154
1155   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1156   ok(rc == 0, "Text marked as modified, expected not modified!\n");
1157
1158   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1159   however, SCF_ALL has been implemented*/
1160   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1161   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1162
1163   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1164   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1165
1166   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1167
1168   /*Select the string "wine"*/
1169   cr.cpMin = 0;
1170   cr.cpMax = 4;
1171   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1172
1173   /*Copy the italicized "wine" to the clipboard*/
1174   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1175
1176   /*Reset the formatting to default*/
1177   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1178   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1179   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1180
1181   /*Clear the text in the control*/
1182   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1183
1184   /*Switch to Plain Text Mode*/
1185   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1186   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
1187
1188   /*Input "wine" again in normal format*/
1189   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1190
1191   /*Paste the italicized "wine" into the control*/
1192   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1193
1194   /*Select a character from the first "wine" string*/
1195   cr.cpMin = 2;
1196   cr.cpMax = 3;
1197   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1198
1199   /*Retrieve its formatting*/
1200   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1201               (LPARAM) &cf2);
1202
1203   /*Select a character from the second "wine" string*/
1204   cr.cpMin = 5;
1205   cr.cpMax = 6;
1206   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1207
1208   /*Retrieve its formatting*/
1209   cf2test.cbSize = sizeof(CHARFORMAT2);
1210   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1211                (LPARAM) &cf2test);
1212
1213   /*Compare the two formattings*/
1214     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1215       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1216        cf2.dwEffects, cf2test.dwEffects);
1217   /*Test TM_RICHTEXT by: switching back to Rich Text mode
1218                          printing "wine" in the current format(normal)
1219                          pasting "wine" from the clipboard(italicized)
1220                          comparing the two formats(should differ)*/
1221
1222   /*Attempt to switch with text in control*/
1223   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1224   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1225
1226   /*Clear control*/
1227   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1228
1229   /*Switch into Rich Text mode*/
1230   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1231   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1232
1233   /*Print "wine" in normal formatting into the control*/
1234   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1235
1236   /*Paste italicized "wine" into the control*/
1237   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1238
1239   /*Select text from the first "wine" string*/
1240   cr.cpMin = 1;
1241   cr.cpMax = 3;
1242   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1243
1244   /*Retrieve its formatting*/
1245   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1246                 (LPARAM) &cf2);
1247
1248   /*Select text from the second "wine" string*/
1249   cr.cpMin = 6;
1250   cr.cpMax = 7;
1251   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1252
1253   /*Retrieve its formatting*/
1254   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1255                 (LPARAM) &cf2test);
1256
1257   /*Test that the two formattings are not the same*/
1258   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1259       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1260       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1261
1262   DestroyWindow(hwndRichEdit);
1263 }
1264
1265 static void test_SETPARAFORMAT(void)
1266 {
1267   HWND hwndRichEdit = new_richedit(NULL);
1268   PARAFORMAT2 fmt;
1269   HRESULT ret;
1270   LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1271   fmt.cbSize = sizeof(PARAFORMAT2);
1272   fmt.dwMask = PFM_ALIGNMENT;
1273   fmt.wAlignment = PFA_LEFT;
1274
1275   ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1276   ok(ret != 0, "expected non-zero got %d\n", ret);
1277
1278   fmt.cbSize = sizeof(PARAFORMAT2);
1279   fmt.dwMask = -1;
1280   ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1281   /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1282    * between richedit different native builds of riched20.dll
1283    * used on different Windows versions. */
1284   ret &= ~PFM_TABLEROWDELIMITER;
1285   fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1286
1287   ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1288   ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1289
1290   DestroyWindow(hwndRichEdit);
1291 }
1292
1293 static void test_TM_PLAINTEXT(void)
1294 {
1295   /*Tests plain text properties*/
1296
1297   HWND hwndRichEdit = new_richedit(NULL);
1298   CHARFORMAT2 cf2, cf2test;
1299   CHARRANGE cr;
1300   int rc = 0;
1301
1302   /*Switch to plain text mode*/
1303
1304   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1305   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1306
1307   /*Fill control with text*/
1308
1309   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1310
1311   /*Select some text and bold it*/
1312
1313   cr.cpMin = 10;
1314   cr.cpMax = 20;
1315   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1316   cf2.cbSize = sizeof(CHARFORMAT2);
1317   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1318
1319   cf2.dwMask = CFM_BOLD | cf2.dwMask;
1320   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1321
1322   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1323   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1324
1325   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1326   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1327
1328   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1329   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1330
1331   /*Get the formatting of those characters*/
1332
1333   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1334
1335   /*Get the formatting of some other characters*/
1336   cf2test.cbSize = sizeof(CHARFORMAT2);
1337   cr.cpMin = 21;
1338   cr.cpMax = 30;
1339   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1340   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1341
1342   /*Test that they are the same as plain text allows only one formatting*/
1343
1344   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1345      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1346      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1347   
1348   /*Fill the control with a "wine" string, which when inserted will be bold*/
1349
1350   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1351
1352   /*Copy the bolded "wine" string*/
1353
1354   cr.cpMin = 0;
1355   cr.cpMax = 4;
1356   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1357   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1358
1359   /*Swap back to rich text*/
1360
1361   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1362   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1363
1364   /*Set the default formatting to bold italics*/
1365
1366   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1367   cf2.dwMask |= CFM_ITALIC;
1368   cf2.dwEffects ^= CFE_ITALIC;
1369   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1370   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1371
1372   /*Set the text in the control to "wine", which will be bold and italicized*/
1373
1374   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1375
1376   /*Paste the plain text "wine" string, which should take the insert
1377    formatting, which at the moment is bold italics*/
1378
1379   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1380
1381   /*Select the first "wine" string and retrieve its formatting*/
1382
1383   cr.cpMin = 1;
1384   cr.cpMax = 3;
1385   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1386   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1387
1388   /*Select the second "wine" string and retrieve its formatting*/
1389
1390   cr.cpMin = 5;
1391   cr.cpMax = 7;
1392   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1393   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1394
1395   /*Compare the two formattings. They should be the same.*/
1396
1397   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1398      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1399      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1400   DestroyWindow(hwndRichEdit);
1401 }
1402
1403 static void test_WM_GETTEXT(void)
1404 {
1405     HWND hwndRichEdit = new_richedit(NULL);
1406     static const char text[] = "Hello. My name is RichEdit!";
1407     static const char text2[] = "Hello. My name is RichEdit!\r";
1408     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1409     char buffer[1024] = {0};
1410     int result;
1411
1412     /* Baseline test with normal-sized buffer */
1413     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1414     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1415     ok(result == lstrlen(buffer),
1416         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1417     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1418     result = strcmp(buffer,text);
1419     ok(result == 0, 
1420         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1421
1422     /* Test for returned value of WM_GETTEXTLENGTH */
1423     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1424     ok(result == lstrlen(text),
1425         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1426         result, lstrlen(text));
1427
1428     /* Test for behavior in overflow case */
1429     memset(buffer, 0, 1024);
1430     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1431     ok(result == 0 ||
1432        result == lstrlenA(text) - 1, /* XP, win2k3 */
1433         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1434     result = strcmp(buffer,text);
1435     if (result)
1436         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1437     ok(result == 0,
1438         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1439
1440     /* Baseline test with normal-sized buffer and carriage return */
1441     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1442     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1443     ok(result == lstrlen(buffer),
1444         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1445     result = strcmp(buffer,text2_after);
1446     ok(result == 0,
1447         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1448
1449     /* Test for returned value of WM_GETTEXTLENGTH */
1450     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1451     ok(result == lstrlen(text2_after),
1452         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1453         result, lstrlen(text2_after));
1454
1455     /* Test for behavior of CRLF conversion in case of overflow */
1456     memset(buffer, 0, 1024);
1457     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1458     ok(result == 0 ||
1459        result == lstrlenA(text2) - 1, /* XP, win2k3 */
1460         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1461     result = strcmp(buffer,text2);
1462     if (result)
1463         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1464     ok(result == 0,
1465         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1466
1467     DestroyWindow(hwndRichEdit);
1468 }
1469
1470 static void test_EM_GETTEXTRANGE(void)
1471 {
1472     HWND hwndRichEdit = new_richedit(NULL);
1473     const char * text1 = "foo bar\r\nfoo bar";
1474     const char * text2 = "foo bar\rfoo bar";
1475     const char * expect = "bar\rfoo";
1476     char buffer[1024] = {0};
1477     LRESULT result;
1478     TEXTRANGEA textRange;
1479
1480     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1481
1482     textRange.lpstrText = buffer;
1483     textRange.chrg.cpMin = 4;
1484     textRange.chrg.cpMax = 11;
1485     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1486     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488
1489     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1490
1491     textRange.lpstrText = buffer;
1492     textRange.chrg.cpMin = 4;
1493     textRange.chrg.cpMax = 11;
1494     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1495     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1496     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1497
1498     /* cpMax of text length is used instead of -1 in this case */
1499     textRange.lpstrText = buffer;
1500     textRange.chrg.cpMin = 0;
1501     textRange.chrg.cpMax = -1;
1502     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1503     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1504     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1505
1506     /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1507     textRange.lpstrText = buffer;
1508     textRange.chrg.cpMin = -1;
1509     textRange.chrg.cpMax = 1;
1510     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1511     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1512     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1513
1514     /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1515     textRange.lpstrText = buffer;
1516     textRange.chrg.cpMin = 1;
1517     textRange.chrg.cpMax = -1;
1518     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1519     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1520     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1521
1522     /* no end character is copied if cpMax - cpMin < 0 */
1523     textRange.lpstrText = buffer;
1524     textRange.chrg.cpMin = 5;
1525     textRange.chrg.cpMax = 5;
1526     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1527     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1528     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1529
1530     /* cpMax of text length is used if cpMax > text length*/
1531     textRange.lpstrText = buffer;
1532     textRange.chrg.cpMin = 0;
1533     textRange.chrg.cpMax = 1000;
1534     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1535     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1536     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1537
1538     DestroyWindow(hwndRichEdit);
1539 }
1540
1541 static void test_EM_GETSELTEXT(void)
1542 {
1543     HWND hwndRichEdit = new_richedit(NULL);
1544     const char * text1 = "foo bar\r\nfoo bar";
1545     const char * text2 = "foo bar\rfoo bar";
1546     const char * expect = "bar\rfoo";
1547     char buffer[1024] = {0};
1548     LRESULT result;
1549
1550     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1551
1552     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1553     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1554     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1555     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1556
1557     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1558
1559     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1560     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1561     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1562     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1563
1564     DestroyWindow(hwndRichEdit);
1565 }
1566
1567 /* FIXME: need to test unimplemented options and robustly test wparam */
1568 static void test_EM_SETOPTIONS(void)
1569 {
1570     HWND hwndRichEdit;
1571     static const char text[] = "Hello. My name is RichEdit!";
1572     char buffer[1024] = {0};
1573     DWORD dwStyle, options, oldOptions;
1574     DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1575                          ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1576                          ES_SELECTIONBAR|ES_VERTICAL;
1577
1578     /* Test initial options. */
1579     hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1580                                 0, 0, 200, 60, NULL, NULL,
1581                                 hmoduleRichEdit, NULL);
1582     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1583        RICHEDIT_CLASS, (int) GetLastError());
1584     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1585     ok(options == 0, "Incorrect initial options %x\n", options);
1586     DestroyWindow(hwndRichEdit);
1587
1588     hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1589                                 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1590                                 0, 0, 200, 60, NULL, NULL,
1591                                 hmoduleRichEdit, NULL);
1592     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1593        RICHEDIT_CLASS, (int) GetLastError());
1594     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1595     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1596     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1597        "Incorrect initial options %x\n", options);
1598
1599     /* NEGATIVE TESTING - NO OPTIONS SET */
1600     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1601     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1602
1603     /* testing no readonly by sending 'a' to the control*/
1604     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1605     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1606     ok(buffer[0]=='a', 
1607        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1608     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1609
1610     /* READONLY - sending 'a' to the control */
1611     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1612     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1613     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1614     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1615     ok(buffer[0]==text[0], 
1616        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1617
1618     /* EM_SETOPTIONS changes the window style, but changing the
1619      * window style does not change the options. */
1620     dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1621     ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1622     SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1623     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1624     ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1625     /* Confirm that the text is still read only. */
1626     SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1627     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1628     ok(buffer[0]==text[0],
1629        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1630
1631     oldOptions = options;
1632     SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1633     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1634     ok(options == oldOptions,
1635        "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1636
1637     DestroyWindow(hwndRichEdit);
1638 }
1639
1640 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1641 {
1642   CHARFORMAT2W text_format;
1643   text_format.cbSize = sizeof(text_format);
1644   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1645   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1646   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1647 }
1648
1649 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1650 {
1651   int link_present = 0;
1652
1653   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1654   if (is_url) 
1655   { /* control text is url; should get CFE_LINK */
1656         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1657   }
1658   else 
1659   {
1660     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1661   }
1662 }
1663
1664 static HWND new_static_wnd(HWND parent) {
1665   return new_window("Static", 0, parent);
1666 }
1667
1668 static void test_EM_AUTOURLDETECT(void)
1669 {
1670   /* DO NOT change the properties of the first two elements. To shorten the
1671      tests, all tests after WM_SETTEXT test just the first two elements -
1672      one non-URL and one URL */
1673   struct urls_s {
1674     const char *text;
1675     int is_url;
1676   } urls[12] = {
1677     {"winehq.org", 0},
1678     {"http://www.winehq.org", 1},
1679     {"http//winehq.org", 0},
1680     {"ww.winehq.org", 0},
1681     {"www.winehq.org", 1},
1682     {"ftp://192.168.1.1", 1},
1683     {"ftp//192.168.1.1", 0},
1684     {"mailto:your@email.com", 1},    
1685     {"prospero:prosperoserver", 1},
1686     {"telnet:test", 1},
1687     {"news:newserver", 1},
1688     {"wais:waisserver", 1}  
1689   };
1690
1691   int i, j;
1692   int urlRet=-1;
1693   HWND hwndRichEdit, parent;
1694
1695   /* All of the following should cause the URL to be detected  */
1696   const char * templates_delim[] = {
1697     "This is some text with X on it",
1698     "This is some text with (X) on it",
1699     "This is some text with X\r on it",
1700     "This is some text with ---X--- on it",
1701     "This is some text with \"X\" on it",
1702     "This is some text with 'X' on it",
1703     "This is some text with 'X' on it",
1704     "This is some text with :X: on it",
1705
1706     "This text ends with X",
1707
1708     "This is some text with X) on it",
1709     "This is some text with X--- on it",
1710     "This is some text with X\" on it",
1711     "This is some text with X' on it",
1712     "This is some text with X: on it",
1713
1714     "This is some text with (X on it",
1715     "This is some text with \rX on it",
1716     "This is some text with ---X on it",
1717     "This is some text with \"X on it",
1718     "This is some text with 'X on it",
1719     "This is some text with :X on it",
1720   };
1721   /* None of these should cause the URL to be detected */
1722   const char * templates_non_delim[] = {
1723     "This is some text with |X| on it",
1724     "This is some text with *X* on it",
1725     "This is some text with /X/ on it",
1726     "This is some text with +X+ on it",
1727     "This is some text with %X% on it",
1728     "This is some text with #X# on it",
1729     "This is some text with @X@ on it",
1730     "This is some text with \\X\\ on it",
1731     "This is some text with |X on it",
1732     "This is some text with *X on it",
1733     "This is some text with /X on it",
1734     "This is some text with +X on it",
1735     "This is some text with %X on it",
1736     "This is some text with #X on it",
1737     "This is some text with @X on it",
1738     "This is some text with \\X on it",
1739   };
1740   /* All of these cause the URL detection to be extended by one more byte,
1741      thus demonstrating that the tested character is considered as part
1742      of the URL. */
1743   const char * templates_xten_delim[] = {
1744     "This is some text with X| on it",
1745     "This is some text with X* on it",
1746     "This is some text with X/ on it",
1747     "This is some text with X+ on it",
1748     "This is some text with X% on it",
1749     "This is some text with X# on it",
1750     "This is some text with X@ on it",
1751     "This is some text with X\\ on it",
1752   };
1753   char buffer[1024];
1754
1755   parent = new_static_wnd(NULL);
1756   hwndRichEdit = new_richedit(parent);
1757   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1758   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1759   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1760   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1761   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1762   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1763   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1764   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1765   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1766   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1767   /* for each url, check the text to see if CFE_LINK effect is present */
1768   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1769
1770     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1771     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1772     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1773
1774     /* Link detection should happen immediately upon WM_SETTEXT */
1775     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1776     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1777     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1778   }
1779   DestroyWindow(hwndRichEdit);
1780
1781   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1782   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1783     hwndRichEdit = new_richedit(parent);
1784
1785     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1786       char * at_pos;
1787       int at_offset;
1788       int end_offset;
1789
1790       at_pos = strchr(templates_delim[j], 'X');
1791       at_offset = at_pos - templates_delim[j];
1792       strncpy(buffer, templates_delim[j], at_offset);
1793       buffer[at_offset] = '\0';
1794       strcat(buffer, urls[i].text);
1795       strcat(buffer, templates_delim[j] + at_offset + 1);
1796       end_offset = at_offset + strlen(urls[i].text);
1797
1798       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1799       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1800
1801       /* This assumes no templates start with the URL itself, and that they
1802          have at least two characters before the URL text */
1803       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1804         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1805       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1806         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1807       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1808         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1809
1810       if (urls[i].is_url)
1811       {
1812         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1813           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1814         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1815           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1816       }
1817       else
1818       {
1819         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1820           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1821         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1822           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1823       }
1824       if (buffer[end_offset] != '\0')
1825       {
1826         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1827           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1828         if (buffer[end_offset +1] != '\0')
1829         {
1830           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1831             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1832         }
1833       }
1834     }
1835
1836     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1837       char * at_pos;
1838       int at_offset;
1839       int end_offset;
1840
1841       at_pos = strchr(templates_non_delim[j], 'X');
1842       at_offset = at_pos - templates_non_delim[j];
1843       strncpy(buffer, templates_non_delim[j], at_offset);
1844       buffer[at_offset] = '\0';
1845       strcat(buffer, urls[i].text);
1846       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1847       end_offset = at_offset + strlen(urls[i].text);
1848
1849       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1850       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1851
1852       /* This assumes no templates start with the URL itself, and that they
1853          have at least two characters before the URL text */
1854       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1855         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1856       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1857         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1858       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1859         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1860
1861       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1862         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1863       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1864         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1865       if (buffer[end_offset] != '\0')
1866       {
1867         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1868           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1869         if (buffer[end_offset +1] != '\0')
1870         {
1871           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1872             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1873         }
1874       }
1875     }
1876
1877     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1878       char * at_pos;
1879       int at_offset;
1880       int end_offset;
1881
1882       at_pos = strchr(templates_xten_delim[j], 'X');
1883       at_offset = at_pos - templates_xten_delim[j];
1884       strncpy(buffer, templates_xten_delim[j], at_offset);
1885       buffer[at_offset] = '\0';
1886       strcat(buffer, urls[i].text);
1887       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1888       end_offset = at_offset + strlen(urls[i].text);
1889
1890       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1891       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1892
1893       /* This assumes no templates start with the URL itself, and that they
1894          have at least two characters before the URL text */
1895       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1901
1902       if (urls[i].is_url)
1903       {
1904         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1905           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1906         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1907           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1908         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1909           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1910       }
1911       else
1912       {
1913         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1914           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1915         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1916           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1917         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1918           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1919       }
1920       if (buffer[end_offset +1] != '\0')
1921       {
1922         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1923           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1924         if (buffer[end_offset +2] != '\0')
1925         {
1926           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1927             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1928         }
1929       }
1930     }
1931
1932     DestroyWindow(hwndRichEdit);
1933     hwndRichEdit = NULL;
1934   }
1935
1936   /* Test detection of URLs within normal text - WM_CHAR case. */
1937   /* Test only the first two URL examples for brevity */
1938   for (i = 0; i < 2; i++) {
1939     hwndRichEdit = new_richedit(parent);
1940
1941     /* Also for brevity, test only the first three delimiters */
1942     for (j = 0; j < 3; j++) {
1943       char * at_pos;
1944       int at_offset;
1945       int end_offset;
1946       int u, v;
1947
1948       at_pos = strchr(templates_delim[j], 'X');
1949       at_offset = at_pos - templates_delim[j];
1950       end_offset = at_offset + strlen(urls[i].text);
1951
1952       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1953       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1954       for (u = 0; templates_delim[j][u]; u++) {
1955         if (templates_delim[j][u] == '\r') {
1956           simulate_typing_characters(hwndRichEdit, "\r");
1957         } else if (templates_delim[j][u] != 'X') {
1958           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1959         } else {
1960           for (v = 0; urls[i].text[v]; v++) {
1961             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1962           }
1963         }
1964       }
1965       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1966
1967       /* This assumes no templates start with the URL itself, and that they
1968          have at least two characters before the URL text */
1969       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1970         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1971       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1972         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1973       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1974         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1975
1976       if (urls[i].is_url)
1977       {
1978         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1980         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1981           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1982       }
1983       else
1984       {
1985         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1986           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1987         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1988           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1989       }
1990       if (buffer[end_offset] != '\0')
1991       {
1992         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1993           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1994         if (buffer[end_offset +1] != '\0')
1995         {
1996           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1997             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1998         }
1999       }
2000
2001       /* The following will insert a paragraph break after the first character
2002          of the URL candidate, thus breaking the URL. It is expected that the
2003          CFE_LINK attribute should break across both pieces of the URL */
2004       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2005       simulate_typing_characters(hwndRichEdit, "\r");
2006       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2007
2008       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2009         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2010       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2011         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2012       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2013         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2014
2015       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017       /* end_offset moved because of paragraph break */
2018       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2019         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2020       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2021       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
2022       {
2023         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2024           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2025         if (buffer[end_offset +2] != '\0')
2026         {
2027           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2028             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2029         }
2030       }
2031
2032       /* The following will remove the just-inserted paragraph break, thus
2033          restoring the URL */
2034       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2035       simulate_typing_characters(hwndRichEdit, "\b");
2036       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2037
2038       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2039         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2040       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2041         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2042       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2043         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2044
2045       if (urls[i].is_url)
2046       {
2047         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2049         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2050           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2051       }
2052       else
2053       {
2054         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2055           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2056         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2057           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2058       }
2059       if (buffer[end_offset] != '\0')
2060       {
2061         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2062           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2063         if (buffer[end_offset +1] != '\0')
2064         {
2065           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2066             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2067         }
2068       }
2069     }
2070     DestroyWindow(hwndRichEdit);
2071     hwndRichEdit = NULL;
2072   }
2073
2074   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2075   /* Test just the first two URL examples for brevity */
2076   for (i = 0; i < 2; i++) {
2077     SETTEXTEX st;
2078
2079     hwndRichEdit = new_richedit(parent);
2080
2081     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2082        be detected:
2083        1) Set entire text, a la WM_SETTEXT
2084        2) Set a selection of the text to the URL
2085        3) Set a portion of the text at a time, which eventually results in
2086           an URL
2087        All of them should give equivalent results
2088      */
2089
2090     /* Set entire text in one go, like WM_SETTEXT */
2091     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2092       char * at_pos;
2093       int at_offset;
2094       int end_offset;
2095
2096       st.codepage = CP_ACP;
2097       st.flags = ST_DEFAULT;
2098
2099       at_pos = strchr(templates_delim[j], 'X');
2100       at_offset = at_pos - templates_delim[j];
2101       strncpy(buffer, templates_delim[j], at_offset);
2102       buffer[at_offset] = '\0';
2103       strcat(buffer, urls[i].text);
2104       strcat(buffer, templates_delim[j] + at_offset + 1);
2105       end_offset = at_offset + strlen(urls[i].text);
2106
2107       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2108       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2109
2110       /* This assumes no templates start with the URL itself, and that they
2111          have at least two characters before the URL text */
2112       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2118
2119       if (urls[i].is_url)
2120       {
2121         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2125       }
2126       else
2127       {
2128         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2132       }
2133       if (buffer[end_offset] != '\0')
2134       {
2135         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137         if (buffer[end_offset +1] != '\0')
2138         {
2139           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2141         }
2142       }
2143     }
2144
2145     /* Set selection with X to the URL */
2146     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2147       char * at_pos;
2148       int at_offset;
2149       int end_offset;
2150
2151       at_pos = strchr(templates_delim[j], 'X');
2152       at_offset = at_pos - templates_delim[j];
2153       end_offset = at_offset + strlen(urls[i].text);
2154
2155       st.codepage = CP_ACP;
2156       st.flags = ST_DEFAULT;
2157       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2158       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2159       st.flags = ST_SELECTION;
2160       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2161       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2162       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2163
2164       /* This assumes no templates start with the URL itself, and that they
2165          have at least two characters before the URL text */
2166       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2167         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2168       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2169         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2170       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2171         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2172
2173       if (urls[i].is_url)
2174       {
2175         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2176           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2177         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2178           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2179       }
2180       else
2181       {
2182         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2183           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2184         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2185           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2186       }
2187       if (buffer[end_offset] != '\0')
2188       {
2189         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2190           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2191         if (buffer[end_offset +1] != '\0')
2192         {
2193           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2194             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2195         }
2196       }
2197     }
2198
2199     /* Set selection with X to the first character of the URL, then the rest */
2200     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2201       char * at_pos;
2202       int at_offset;
2203       int end_offset;
2204
2205       at_pos = strchr(templates_delim[j], 'X');
2206       at_offset = at_pos - templates_delim[j];
2207       end_offset = at_offset + strlen(urls[i].text);
2208
2209       strcpy(buffer, "YY");
2210       buffer[0] = urls[i].text[0];
2211
2212       st.codepage = CP_ACP;
2213       st.flags = ST_DEFAULT;
2214       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2215       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2216       st.flags = ST_SELECTION;
2217       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2218       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2219       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2220       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2221       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2222
2223       /* This assumes no templates start with the URL itself, and that they
2224          have at least two characters before the URL text */
2225       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2226         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2227       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2228         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2229       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2230         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2231
2232       if (urls[i].is_url)
2233       {
2234         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2235           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2236         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2237           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2238       }
2239       else
2240       {
2241         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2242           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2243         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2244           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2245       }
2246       if (buffer[end_offset] != '\0')
2247       {
2248         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2249           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2250         if (buffer[end_offset +1] != '\0')
2251         {
2252           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2253             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2254         }
2255       }
2256     }
2257
2258     DestroyWindow(hwndRichEdit);
2259     hwndRichEdit = NULL;
2260   }
2261
2262   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2263   /* Test just the first two URL examples for brevity */
2264   for (i = 0; i < 2; i++) {
2265     hwndRichEdit = new_richedit(parent);
2266
2267     /* Set selection with X to the URL */
2268     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2269       char * at_pos;
2270       int at_offset;
2271       int end_offset;
2272
2273       at_pos = strchr(templates_delim[j], 'X');
2274       at_offset = at_pos - templates_delim[j];
2275       end_offset = at_offset + strlen(urls[i].text);
2276
2277       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2278       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2279       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2280       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2281       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2282
2283       /* This assumes no templates start with the URL itself, and that they
2284          have at least two characters before the URL text */
2285       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2286         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2287       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2288         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2289       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2290         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2291
2292       if (urls[i].is_url)
2293       {
2294         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2295           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2296         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2297           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2298       }
2299       else
2300       {
2301         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2302           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2303         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2304           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2305       }
2306       if (buffer[end_offset] != '\0')
2307       {
2308         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2309           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2310         if (buffer[end_offset +1] != '\0')
2311         {
2312           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2313             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2314         }
2315       }
2316     }
2317
2318     /* Set selection with X to the first character of the URL, then the rest */
2319     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2320       char * at_pos;
2321       int at_offset;
2322       int end_offset;
2323
2324       at_pos = strchr(templates_delim[j], 'X');
2325       at_offset = at_pos - templates_delim[j];
2326       end_offset = at_offset + strlen(urls[i].text);
2327
2328       strcpy(buffer, "YY");
2329       buffer[0] = urls[i].text[0];
2330
2331       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2332       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2333       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2334       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2335       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2336       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2337       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2338
2339       /* This assumes no templates start with the URL itself, and that they
2340          have at least two characters before the URL text */
2341       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2342         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2343       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2344         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2345       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2346         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2347
2348       if (urls[i].is_url)
2349       {
2350         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2351           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2352         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2353           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2354       }
2355       else
2356       {
2357         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2358           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2359         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2360           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2361       }
2362       if (buffer[end_offset] != '\0')
2363       {
2364         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2365           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2366         if (buffer[end_offset +1] != '\0')
2367         {
2368           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2369             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2370         }
2371       }
2372     }
2373
2374     DestroyWindow(hwndRichEdit);
2375     hwndRichEdit = NULL;
2376   }
2377
2378   DestroyWindow(parent);
2379 }
2380
2381 static void test_EM_SCROLL(void)
2382 {
2383   int i, j;
2384   int r; /* return value */
2385   int expr; /* expected return value */
2386   HWND hwndRichEdit = new_richedit(NULL);
2387   int y_before, y_after; /* units of lines of text */
2388
2389   /* test a richedit box containing a single line of text */
2390   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2391   expr = 0x00010000;
2392   for (i = 0; i < 4; i++) {
2393     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2394
2395     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2396     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2397     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2398        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2399     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2400        "(i == %d)\n", y_after, i);
2401   }
2402
2403   /*
2404    * test a richedit box that will scroll. There are two general
2405    * cases: the case without any long lines and the case with a long
2406    * line.
2407    */
2408   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2409     if (i == 0)
2410       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2411     else
2412       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2413                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2414                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2415                   "LONG LINE \nb\nc\nd\ne");
2416     for (j = 0; j < 12; j++) /* reset scroll position to top */
2417       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2418
2419     /* get first visible line */
2420     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2422
2423     /* get new current first visible line */
2424     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2425
2426     ok(((r & 0xffffff00) == 0x00010000) &&
2427        ((r & 0x000000ff) != 0x00000000),
2428        "EM_SCROLL page down didn't scroll by a small positive number of "
2429        "lines (r == 0x%08x)\n", r);
2430     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2431        "(line %d scrolled to line %d\n", y_before, y_after);
2432
2433     y_before = y_after;
2434     
2435     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2436     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2437     ok(((r & 0xffffff00) == 0x0001ff00),
2438        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2439        "(r == 0x%08x)\n", r);
2440     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2441        "%d scrolled to line %d\n", y_before, y_after);
2442     
2443     y_before = y_after;
2444
2445     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2446
2447     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2448
2449     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2450        "(r == 0x%08x)\n", r);
2451     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2452        "1 line (%d scrolled to %d)\n", y_before, y_after);
2453
2454     y_before = y_after;
2455
2456     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2457
2458     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2459
2460     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2461        "(r == 0x%08x)\n", r);
2462     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2463        "line (%d scrolled to %d)\n", y_before, y_after);
2464
2465     y_before = y_after;
2466
2467     r = SendMessage(hwndRichEdit, EM_SCROLL,
2468                     SB_LINEUP, 0); /* lineup beyond top */
2469
2470     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2471
2472     ok(r == 0x00010000,
2473        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2474     ok(y_before == y_after,
2475        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2476
2477     y_before = y_after;
2478
2479     r = SendMessage(hwndRichEdit, EM_SCROLL,
2480                     SB_PAGEUP, 0);/*page up beyond top */
2481
2482     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2483
2484     ok(r == 0x00010000,
2485        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2486     ok(y_before == y_after,
2487        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2488
2489     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2490       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2491     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2492     r = SendMessage(hwndRichEdit, EM_SCROLL,
2493                     SB_PAGEDOWN, 0); /* page down beyond bot */
2494     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2495
2496     ok(r == 0x00010000,
2497        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2498     ok(y_before == y_after,
2499        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2500        y_before, y_after);
2501
2502     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2503     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2504     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2505
2506     ok(r == 0x00010000,
2507        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2508     ok(y_before == y_after,
2509        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2510        y_before, y_after);
2511   }
2512   DestroyWindow(hwndRichEdit);
2513 }
2514
2515 static unsigned int recursionLevel = 0;
2516 static unsigned int WM_SIZE_recursionLevel = 0;
2517 static BOOL bailedOutOfRecursion = FALSE;
2518 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2519
2520 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2521 {
2522     LRESULT r;
2523
2524     if (bailedOutOfRecursion) return 0;
2525     if (recursionLevel >= 32) {
2526         bailedOutOfRecursion = TRUE;
2527         return 0;
2528     }
2529
2530     recursionLevel++;
2531     switch (message) {
2532     case WM_SIZE:
2533         WM_SIZE_recursionLevel++;
2534         r = richeditProc(hwnd, message, wParam, lParam);
2535         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2536         ShowScrollBar(hwnd, SB_VERT, TRUE);
2537         WM_SIZE_recursionLevel--;
2538         break;
2539     default:
2540         r = richeditProc(hwnd, message, wParam, lParam);
2541         break;
2542     }
2543     recursionLevel--;
2544     return r;
2545 }
2546
2547 static void test_scrollbar_visibility(void)
2548 {
2549   HWND hwndRichEdit;
2550   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2551   SCROLLINFO si;
2552   WNDCLASSA cls;
2553   BOOL r;
2554
2555   /* These tests show that richedit should temporarily refrain from automatically
2556      hiding or showing its scrollbars (vertical at least) when an explicit request
2557      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2558      Some applications depend on forced showing (when otherwise richedit would
2559      hide the vertical scrollbar) and are thrown on an endless recursive loop
2560      if richedit auto-hides the scrollbar again. Apparently they never heard of
2561      the ES_DISABLENOSCROLL style... */
2562
2563   hwndRichEdit = new_richedit(NULL);
2564
2565   /* Test default scrollbar visibility behavior */
2566   memset(&si, 0, sizeof(si));
2567   si.cbSize = sizeof(si);
2568   si.fMask = SIF_PAGE | SIF_RANGE;
2569   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571     "Vertical scrollbar is visible, should be invisible.\n");
2572   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2573         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2574         si.nPage, si.nMin, si.nMax);
2575
2576   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2577   memset(&si, 0, sizeof(si));
2578   si.cbSize = sizeof(si);
2579   si.fMask = SIF_PAGE | SIF_RANGE;
2580   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582     "Vertical scrollbar is visible, should be invisible.\n");
2583   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2584         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2585         si.nPage, si.nMin, si.nMax);
2586
2587   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2588   memset(&si, 0, sizeof(si));
2589   si.cbSize = sizeof(si);
2590   si.fMask = SIF_PAGE | SIF_RANGE;
2591   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2593     "Vertical scrollbar is invisible, should be visible.\n");
2594   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596         si.nPage, si.nMin, si.nMax);
2597
2598   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2599      even though it hides the scrollbar */
2600   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601   memset(&si, 0, sizeof(si));
2602   si.cbSize = sizeof(si);
2603   si.fMask = SIF_PAGE | SIF_RANGE;
2604   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2606     "Vertical scrollbar is visible, should be invisible.\n");
2607   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2608         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2609         si.nPage, si.nMin, si.nMax);
2610
2611   /* Setting non-scrolling text again does *not* reset scrollbar range */
2612   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2613   memset(&si, 0, sizeof(si));
2614   si.cbSize = sizeof(si);
2615   si.fMask = SIF_PAGE | SIF_RANGE;
2616   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2617   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2618     "Vertical scrollbar is visible, should be invisible.\n");
2619   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2620         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2621         si.nPage, si.nMin, si.nMax);
2622
2623   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624   memset(&si, 0, sizeof(si));
2625   si.cbSize = sizeof(si);
2626   si.fMask = SIF_PAGE | SIF_RANGE;
2627   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2629     "Vertical scrollbar is visible, should be invisible.\n");
2630   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2631         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632         si.nPage, si.nMin, si.nMax);
2633
2634   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2635   memset(&si, 0, sizeof(si));
2636   si.cbSize = sizeof(si);
2637   si.fMask = SIF_PAGE | SIF_RANGE;
2638   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2640     "Vertical scrollbar is visible, should be invisible.\n");
2641   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2642         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2643         si.nPage, si.nMin, si.nMax);
2644
2645   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2646   memset(&si, 0, sizeof(si));
2647   si.cbSize = sizeof(si);
2648   si.fMask = SIF_PAGE | SIF_RANGE;
2649   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2651     "Vertical scrollbar is visible, should be invisible.\n");
2652   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2653         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2654         si.nPage, si.nMin, si.nMax);
2655
2656   DestroyWindow(hwndRichEdit);
2657
2658   /* Test again, with ES_DISABLENOSCROLL style */
2659   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2660
2661   /* Test default scrollbar visibility behavior */
2662   memset(&si, 0, sizeof(si));
2663   si.cbSize = sizeof(si);
2664   si.fMask = SIF_PAGE | SIF_RANGE;
2665   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2666   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2667     "Vertical scrollbar is invisible, should be visible.\n");
2668   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2669         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2670         si.nPage, si.nMin, si.nMax);
2671
2672   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2673   memset(&si, 0, sizeof(si));
2674   si.cbSize = sizeof(si);
2675   si.fMask = SIF_PAGE | SIF_RANGE;
2676   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2677   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2678     "Vertical scrollbar is invisible, should be visible.\n");
2679   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2680         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2681         si.nPage, si.nMin, si.nMax);
2682
2683   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2684   memset(&si, 0, sizeof(si));
2685   si.cbSize = sizeof(si);
2686   si.fMask = SIF_PAGE | SIF_RANGE;
2687   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2689     "Vertical scrollbar is invisible, should be visible.\n");
2690   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2691         "reported page/range is %d (%d..%d)\n",
2692         si.nPage, si.nMin, si.nMax);
2693
2694   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2695   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2696   memset(&si, 0, sizeof(si));
2697   si.cbSize = sizeof(si);
2698   si.fMask = SIF_PAGE | SIF_RANGE;
2699   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2700   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2701     "Vertical scrollbar is invisible, should be visible.\n");
2702   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2703         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2704         si.nPage, si.nMin, si.nMax);
2705
2706   /* Setting non-scrolling text again does *not* reset scrollbar range */
2707   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2708   memset(&si, 0, sizeof(si));
2709   si.cbSize = sizeof(si);
2710   si.fMask = SIF_PAGE | SIF_RANGE;
2711   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2712   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2713     "Vertical scrollbar is invisible, should be visible.\n");
2714   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2715         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2716         si.nPage, si.nMin, si.nMax);
2717
2718   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2719   memset(&si, 0, sizeof(si));
2720   si.cbSize = sizeof(si);
2721   si.fMask = SIF_PAGE | SIF_RANGE;
2722   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2723   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2724     "Vertical scrollbar is invisible, should be visible.\n");
2725   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2726         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2727         si.nPage, si.nMin, si.nMax);
2728
2729   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2730   memset(&si, 0, sizeof(si));
2731   si.cbSize = sizeof(si);
2732   si.fMask = SIF_PAGE | SIF_RANGE;
2733   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2734   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2735     "Vertical scrollbar is invisible, should be visible.\n");
2736   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2737         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2738         si.nPage, si.nMin, si.nMax);
2739
2740   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2741   memset(&si, 0, sizeof(si));
2742   si.cbSize = sizeof(si);
2743   si.fMask = SIF_PAGE | SIF_RANGE;
2744   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746     "Vertical scrollbar is invisible, should be visible.\n");
2747   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2748         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2749         si.nPage, si.nMin, si.nMax);
2750
2751   DestroyWindow(hwndRichEdit);
2752
2753   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2754   hwndRichEdit = new_richedit(NULL);
2755
2756   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2757   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2758   memset(&si, 0, sizeof(si));
2759   si.cbSize = sizeof(si);
2760   si.fMask = SIF_PAGE | SIF_RANGE;
2761   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2762   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2763     "Vertical scrollbar is invisible, should be visible.\n");
2764   todo_wine {
2765   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2766         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2767         si.nPage, si.nMin, si.nMax);
2768   }
2769
2770   /* Ditto, see above */
2771   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2772   memset(&si, 0, sizeof(si));
2773   si.cbSize = sizeof(si);
2774   si.fMask = SIF_PAGE | SIF_RANGE;
2775   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2776   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2777     "Vertical scrollbar is invisible, should be visible.\n");
2778   todo_wine {
2779   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2780         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2781         si.nPage, si.nMin, si.nMax);
2782   }
2783
2784   /* Ditto, see above */
2785   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2786   memset(&si, 0, sizeof(si));
2787   si.cbSize = sizeof(si);
2788   si.fMask = SIF_PAGE | SIF_RANGE;
2789   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2790   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2791     "Vertical scrollbar is invisible, should be visible.\n");
2792   todo_wine {
2793   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2794         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2795         si.nPage, si.nMin, si.nMax);
2796   }
2797
2798   /* Ditto, see above */
2799   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2800   memset(&si, 0, sizeof(si));
2801   si.cbSize = sizeof(si);
2802   si.fMask = SIF_PAGE | SIF_RANGE;
2803   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2804   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2805     "Vertical scrollbar is invisible, should be visible.\n");
2806   todo_wine {
2807   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2808         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2809         si.nPage, si.nMin, si.nMax);
2810   }
2811
2812   /* Ditto, see above */
2813   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2814   memset(&si, 0, sizeof(si));
2815   si.cbSize = sizeof(si);
2816   si.fMask = SIF_PAGE | SIF_RANGE;
2817   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2818   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2819     "Vertical scrollbar is invisible, should be visible.\n");
2820   todo_wine {
2821   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2822         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2823         si.nPage, si.nMin, si.nMax);
2824   }
2825
2826   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2828   memset(&si, 0, sizeof(si));
2829   si.cbSize = sizeof(si);
2830   si.fMask = SIF_PAGE | SIF_RANGE;
2831   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2832   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2833     "Vertical scrollbar is visible, should be invisible.\n");
2834   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2835         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2836         si.nPage, si.nMin, si.nMax);
2837
2838   DestroyWindow(hwndRichEdit);
2839
2840   hwndRichEdit = new_richedit(NULL);
2841
2842   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2843   memset(&si, 0, sizeof(si));
2844   si.cbSize = sizeof(si);
2845   si.fMask = SIF_PAGE | SIF_RANGE;
2846   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2847   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2848     "Vertical scrollbar is visible, should be invisible.\n");
2849   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2850         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2851         si.nPage, si.nMin, si.nMax);
2852
2853   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2854   memset(&si, 0, sizeof(si));
2855   si.cbSize = sizeof(si);
2856   si.fMask = SIF_PAGE | SIF_RANGE;
2857   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2858   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2859     "Vertical scrollbar is visible, should be invisible.\n");
2860   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2861         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2862         si.nPage, si.nMin, si.nMax);
2863
2864   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2865   memset(&si, 0, sizeof(si));
2866   si.cbSize = sizeof(si);
2867   si.fMask = SIF_PAGE | SIF_RANGE;
2868   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2869   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2870     "Vertical scrollbar is visible, should be invisible.\n");
2871   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2872         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2873         si.nPage, si.nMin, si.nMax);
2874
2875   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2876   memset(&si, 0, sizeof(si));
2877   si.cbSize = sizeof(si);
2878   si.fMask = SIF_PAGE | SIF_RANGE;
2879   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2881     "Vertical scrollbar is visible, should be invisible.\n");
2882   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884         si.nPage, si.nMin, si.nMax);
2885
2886   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2887   memset(&si, 0, sizeof(si));
2888   si.cbSize = sizeof(si);
2889   si.fMask = SIF_PAGE | SIF_RANGE;
2890   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892     "Vertical scrollbar is invisible, should be visible.\n");
2893   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894         "reported page/range is %d (%d..%d)\n",
2895         si.nPage, si.nMin, si.nMax);
2896
2897   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2898   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2899   memset(&si, 0, sizeof(si));
2900   si.cbSize = sizeof(si);
2901   si.fMask = SIF_PAGE | SIF_RANGE;
2902   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2904     "Vertical scrollbar is visible, should be invisible.\n");
2905   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2906         "reported page/range is %d (%d..%d)\n",
2907         si.nPage, si.nMin, si.nMax);
2908
2909   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2910   memset(&si, 0, sizeof(si));
2911   si.cbSize = sizeof(si);
2912   si.fMask = SIF_PAGE | SIF_RANGE;
2913   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915     "Vertical scrollbar is visible, should be invisible.\n");
2916   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917         "reported page/range is %d (%d..%d)\n",
2918         si.nPage, si.nMin, si.nMax);
2919
2920   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2921      EM_SCROLL will make visible any forcefully invisible scrollbar */
2922   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2923   memset(&si, 0, sizeof(si));
2924   si.cbSize = sizeof(si);
2925   si.fMask = SIF_PAGE | SIF_RANGE;
2926   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2927   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2928     "Vertical scrollbar is invisible, should be visible.\n");
2929   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2930         "reported page/range is %d (%d..%d)\n",
2931         si.nPage, si.nMin, si.nMax);
2932
2933   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2934   memset(&si, 0, sizeof(si));
2935   si.cbSize = sizeof(si);
2936   si.fMask = SIF_PAGE | SIF_RANGE;
2937   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2938   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2939     "Vertical scrollbar is visible, should be invisible.\n");
2940   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2941         "reported page/range is %d (%d..%d)\n",
2942         si.nPage, si.nMin, si.nMax);
2943
2944   /* Again, EM_SCROLL, with SB_LINEUP */
2945   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2946   memset(&si, 0, sizeof(si));
2947   si.cbSize = sizeof(si);
2948   si.fMask = SIF_PAGE | SIF_RANGE;
2949   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2950   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2951     "Vertical scrollbar is invisible, should be visible.\n");
2952   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2953         "reported page/range is %d (%d..%d)\n",
2954         si.nPage, si.nMin, si.nMax);
2955
2956   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2957   memset(&si, 0, sizeof(si));
2958   si.cbSize = sizeof(si);
2959   si.fMask = SIF_PAGE | SIF_RANGE;
2960   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2962     "Vertical scrollbar is visible, should be invisible.\n");
2963   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2964         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2965         si.nPage, si.nMin, si.nMax);
2966
2967   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2968   memset(&si, 0, sizeof(si));
2969   si.cbSize = sizeof(si);
2970   si.fMask = SIF_PAGE | SIF_RANGE;
2971   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2973     "Vertical scrollbar is invisible, should be visible.\n");
2974   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2975         "reported page/range is %d (%d..%d)\n",
2976         si.nPage, si.nMin, si.nMax);
2977
2978   DestroyWindow(hwndRichEdit);
2979
2980
2981   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2982   hwndRichEdit = new_richedit(NULL);
2983
2984 #define ENABLE_WS_VSCROLL(hwnd) \
2985     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2986 #define DISABLE_WS_VSCROLL(hwnd) \
2987     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2988
2989   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2990   ENABLE_WS_VSCROLL(hwndRichEdit);
2991   memset(&si, 0, sizeof(si));
2992   si.cbSize = sizeof(si);
2993   si.fMask = SIF_PAGE | SIF_RANGE;
2994   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996     "Vertical scrollbar is invisible, should be visible.\n");
2997   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2998         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2999         si.nPage, si.nMin, si.nMax);
3000
3001   /* Ditto, see above */
3002   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3003   memset(&si, 0, sizeof(si));
3004   si.cbSize = sizeof(si);
3005   si.fMask = SIF_PAGE | SIF_RANGE;
3006   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008     "Vertical scrollbar is invisible, should be visible.\n");
3009   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3010         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3011         si.nPage, si.nMin, si.nMax);
3012
3013   /* Ditto, see above */
3014   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3015   memset(&si, 0, sizeof(si));
3016   si.cbSize = sizeof(si);
3017   si.fMask = SIF_PAGE | SIF_RANGE;
3018   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3020     "Vertical scrollbar is invisible, should be visible.\n");
3021   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3022         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3023         si.nPage, si.nMin, si.nMax);
3024
3025   /* Ditto, see above */
3026   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3027   memset(&si, 0, sizeof(si));
3028   si.cbSize = sizeof(si);
3029   si.fMask = SIF_PAGE | SIF_RANGE;
3030   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3032     "Vertical scrollbar is invisible, should be visible.\n");
3033   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035         si.nPage, si.nMin, si.nMax);
3036
3037   /* Ditto, see above */
3038   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3039   memset(&si, 0, sizeof(si));
3040   si.cbSize = sizeof(si);
3041   si.fMask = SIF_PAGE | SIF_RANGE;
3042   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3043   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3044     "Vertical scrollbar is invisible, should be visible.\n");
3045   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3046         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3047         si.nPage, si.nMin, si.nMax);
3048
3049   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3050   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3051   memset(&si, 0, sizeof(si));
3052   si.cbSize = sizeof(si);
3053   si.fMask = SIF_PAGE | SIF_RANGE;
3054   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3055   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3056     "Vertical scrollbar is visible, should be invisible.\n");
3057   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3058         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3059         si.nPage, si.nMin, si.nMax);
3060
3061   DestroyWindow(hwndRichEdit);
3062
3063   hwndRichEdit = new_richedit(NULL);
3064
3065   DISABLE_WS_VSCROLL(hwndRichEdit);
3066   memset(&si, 0, sizeof(si));
3067   si.cbSize = sizeof(si);
3068   si.fMask = SIF_PAGE | SIF_RANGE;
3069   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3070   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3071     "Vertical scrollbar is visible, should be invisible.\n");
3072   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3073         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3074         si.nPage, si.nMin, si.nMax);
3075
3076   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3077   memset(&si, 0, sizeof(si));
3078   si.cbSize = sizeof(si);
3079   si.fMask = SIF_PAGE | SIF_RANGE;
3080   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3081   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3082     "Vertical scrollbar is visible, should be invisible.\n");
3083   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3084         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3085         si.nPage, si.nMin, si.nMax);
3086
3087   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3088   memset(&si, 0, sizeof(si));
3089   si.cbSize = sizeof(si);
3090   si.fMask = SIF_PAGE | SIF_RANGE;
3091   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3092   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3093     "Vertical scrollbar is visible, should be invisible.\n");
3094   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3095         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3096         si.nPage, si.nMin, si.nMax);
3097
3098   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3099   memset(&si, 0, sizeof(si));
3100   si.cbSize = sizeof(si);
3101   si.fMask = SIF_PAGE | SIF_RANGE;
3102   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3103   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3104     "Vertical scrollbar is visible, should be invisible.\n");
3105   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3106         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3107         si.nPage, si.nMin, si.nMax);
3108
3109   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3110   memset(&si, 0, sizeof(si));
3111   si.cbSize = sizeof(si);
3112   si.fMask = SIF_PAGE | SIF_RANGE;
3113   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3114   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3115     "Vertical scrollbar is invisible, should be visible.\n");
3116   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3117         "reported page/range is %d (%d..%d)\n",
3118         si.nPage, si.nMin, si.nMax);
3119
3120   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3121   DISABLE_WS_VSCROLL(hwndRichEdit);
3122   memset(&si, 0, sizeof(si));
3123   si.cbSize = sizeof(si);
3124   si.fMask = SIF_PAGE | SIF_RANGE;
3125   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3126   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3127     "Vertical scrollbar is visible, should be invisible.\n");
3128   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3129         "reported page/range is %d (%d..%d)\n",
3130         si.nPage, si.nMin, si.nMax);
3131
3132   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3133   memset(&si, 0, sizeof(si));
3134   si.cbSize = sizeof(si);
3135   si.fMask = SIF_PAGE | SIF_RANGE;
3136   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3137   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3138     "Vertical scrollbar is visible, should be invisible.\n");
3139   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3140         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3141         si.nPage, si.nMin, si.nMax);
3142
3143   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3144   memset(&si, 0, sizeof(si));
3145   si.cbSize = sizeof(si);
3146   si.fMask = SIF_PAGE | SIF_RANGE;
3147   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3148   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3149     "Vertical scrollbar is invisible, should be visible.\n");
3150   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3151         "reported page/range is %d (%d..%d)\n",
3152         si.nPage, si.nMin, si.nMax);
3153
3154   DISABLE_WS_VSCROLL(hwndRichEdit);
3155   memset(&si, 0, sizeof(si));
3156   si.cbSize = sizeof(si);
3157   si.fMask = SIF_PAGE | SIF_RANGE;
3158   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3159   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3160     "Vertical scrollbar is visible, should be invisible.\n");
3161   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3162         "reported page/range is %d (%d..%d)\n",
3163         si.nPage, si.nMin, si.nMax);
3164
3165   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3166      EM_SCROLL will make visible any forcefully invisible scrollbar */
3167   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3168   memset(&si, 0, sizeof(si));
3169   si.cbSize = sizeof(si);
3170   si.fMask = SIF_PAGE | SIF_RANGE;
3171   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3172   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3173     "Vertical scrollbar is invisible, should be visible.\n");
3174   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3175         "reported page/range is %d (%d..%d)\n",
3176         si.nPage, si.nMin, si.nMax);
3177
3178   DISABLE_WS_VSCROLL(hwndRichEdit);
3179   memset(&si, 0, sizeof(si));
3180   si.cbSize = sizeof(si);
3181   si.fMask = SIF_PAGE | SIF_RANGE;
3182   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3183   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3184     "Vertical scrollbar is visible, should be invisible.\n");
3185   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3186         "reported page/range is %d (%d..%d)\n",
3187         si.nPage, si.nMin, si.nMax);
3188
3189   /* Again, EM_SCROLL, with SB_LINEUP */
3190   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3191   memset(&si, 0, sizeof(si));
3192   si.cbSize = sizeof(si);
3193   si.fMask = SIF_PAGE | SIF_RANGE;
3194   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3195   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3196     "Vertical scrollbar is invisible, should be visible.\n");
3197   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3198         "reported page/range is %d (%d..%d)\n",
3199         si.nPage, si.nMin, si.nMax);
3200
3201   DestroyWindow(hwndRichEdit);
3202
3203   /* This window proc models what is going on with Corman Lisp 3.0.
3204      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3205      force the scrollbar into visibility. Recursion should NOT happen
3206      as a result of this action.
3207    */
3208   r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3209   if (r) {
3210     richeditProc = cls.lpfnWndProc;
3211     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3212     cls.lpszClassName = "RicheditStupidOverride";
3213     if(!RegisterClassA(&cls)) assert(0);
3214
3215     recursionLevel = 0;
3216     WM_SIZE_recursionLevel = 0;
3217     bailedOutOfRecursion = FALSE;
3218     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3219     ok(!bailedOutOfRecursion,
3220         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3221
3222     recursionLevel = 0;
3223     WM_SIZE_recursionLevel = 0;
3224     bailedOutOfRecursion = FALSE;
3225     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3226     ok(!bailedOutOfRecursion,
3227         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3228
3229     /* Unblock window in order to process WM_DESTROY */
3230     recursionLevel = 0;
3231     bailedOutOfRecursion = FALSE;
3232     WM_SIZE_recursionLevel = 0;
3233     DestroyWindow(hwndRichEdit);
3234   }
3235 }
3236
3237 static void test_EM_SETUNDOLIMIT(void)
3238 {
3239   /* cases we test for:
3240    * default behaviour - limiting at 100 undo's 
3241    * undo disabled - setting a limit of 0
3242    * undo limited -  undo limit set to some to some number, like 2
3243    * bad input - sending a negative number should default to 100 undo's */
3244  
3245   HWND hwndRichEdit = new_richedit(NULL);
3246   CHARRANGE cr;
3247   int i;
3248   int result;
3249   
3250   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3251   cr.cpMin = 0;
3252   cr.cpMax = 1;
3253   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3254     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3255       also, multiple pastes don't combine like WM_CHAR would */
3256   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3257
3258   /* first case - check the default */
3259   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3260   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3261     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3262   for (i=0; i<100; i++) /* Undo 100 of them */
3263     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3264   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3265      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3266
3267   /* second case - cannot undo */
3268   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3269   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
3270   SendMessage(hwndRichEdit,
3271               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3272   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3273      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3274
3275   /* third case - set it to an arbitrary number */
3276   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3277   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
3278   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3279   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3280   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3281   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3282   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3283      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3284   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3285   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3286      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3287   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3288   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3289      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3290   
3291   /* fourth case - setting negative numbers should default to 100 undos */
3292   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3293   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3294   ok (result == 100, 
3295       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3296       
3297   DestroyWindow(hwndRichEdit);
3298 }
3299
3300 static void test_ES_PASSWORD(void)
3301 {
3302   /* This isn't hugely testable, so we're just going to run it through its paces */
3303
3304   HWND hwndRichEdit = new_richedit(NULL);
3305   WCHAR result;
3306
3307   /* First, check the default of a regular control */
3308   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3309   ok (result == 0,
3310         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3311
3312   /* Now, set it to something normal */
3313   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3314   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3315   ok (result == 120,
3316         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3317
3318   /* Now, set it to something odd */
3319   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3320   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3321   ok (result == 1234,
3322         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3323   DestroyWindow(hwndRichEdit);
3324 }
3325
3326 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3327                                          LPBYTE pbBuff,
3328                                          LONG cb,
3329                                          LONG *pcb)
3330 {
3331   char** str = (char**)dwCookie;
3332   *pcb = cb;
3333   if (*pcb > 0) {
3334     memcpy(*str, pbBuff, *pcb);
3335     *str += *pcb;
3336   }
3337   return 0;
3338 }
3339
3340 static void test_WM_SETTEXT(void)
3341 {
3342   HWND hwndRichEdit = new_richedit(NULL);
3343   const char * TestItem1 = "TestSomeText";
3344   const char * TestItem2 = "TestSomeText\r";
3345   const char * TestItem2_after = "TestSomeText\r\n";
3346   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3347   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3348   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3349   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3350   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3351   const char * TestItem5_after = "TestSomeText TestSomeText";
3352   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3353   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3354   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3355   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3356
3357   const char rtftextA[] = "{\\rtf sometext}";
3358   const char urtftextA[] = "{\\urtf sometext}";
3359   const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3360   const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3361   const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3362
3363   char buf[1024] = {0};
3364   WCHAR bufW[1024] = {0};
3365   LRESULT result;
3366
3367   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3368      any solitary \r to be converted to \r\n on return. Properly paired
3369      \r\n are not affected. It also shows that the special sequence \r\r\n
3370      gets converted to a single space.
3371    */
3372
3373 #define TEST_SETTEXT(a, b) \
3374   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3375   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3376   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3377   ok (result == lstrlen(buf), \
3378         "WM_GETTEXT returned %ld instead of expected %u\n", \
3379         result, lstrlen(buf)); \
3380   result = strcmp(b, buf); \
3381   ok(result == 0, \
3382         "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3383
3384   TEST_SETTEXT(TestItem1, TestItem1)
3385   TEST_SETTEXT(TestItem2, TestItem2_after)
3386   TEST_SETTEXT(TestItem3, TestItem3_after)
3387   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3388   TEST_SETTEXT(TestItem4, TestItem4_after)
3389   TEST_SETTEXT(TestItem5, TestItem5_after)
3390   TEST_SETTEXT(TestItem6, TestItem6_after)
3391   TEST_SETTEXT(TestItem7, TestItem7_after)
3392
3393   /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3394   TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3395   TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3396   TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3397   TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3398   DestroyWindow(hwndRichEdit);
3399 #undef TEST_SETTEXT
3400
3401 #define TEST_SETTEXTW(a, b) \
3402   result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3403   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3404   result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3405   ok (result == lstrlenW(bufW), \
3406         "WM_GETTEXT returned %ld instead of expected %u\n", \
3407         result, lstrlenW(bufW)); \
3408   result = lstrcmpW(b, bufW); \
3409   ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3410
3411   hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3412                                ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3413                                0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3414   ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3415   TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3416   TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3417   TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3418   TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3419   DestroyWindow(hwndRichEdit);
3420 #undef TEST_SETTEXTW
3421 }
3422
3423 static void test_EM_STREAMOUT(void)
3424 {
3425   HWND hwndRichEdit = new_richedit(NULL);
3426   int r;
3427   EDITSTREAM es;
3428   char buf[1024] = {0};
3429   char * p;
3430
3431   const char * TestItem1 = "TestSomeText";
3432   const char * TestItem2 = "TestSomeText\r";
3433   const char * TestItem3 = "TestSomeText\r\n";
3434
3435   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3436   p = buf;
3437   es.dwCookie = (DWORD_PTR)&p;
3438   es.dwError = 0;
3439   es.pfnCallback = test_WM_SETTEXT_esCallback;
3440   memset(buf, 0, sizeof(buf));
3441   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3442   r = strlen(buf);
3443   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3444   ok(strcmp(buf, TestItem1) == 0,
3445         "streamed text different, got %s\n", buf);
3446
3447   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3448   p = buf;
3449   es.dwCookie = (DWORD_PTR)&p;
3450   es.dwError = 0;
3451   es.pfnCallback = test_WM_SETTEXT_esCallback;
3452   memset(buf, 0, sizeof(buf));
3453   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3454   r = strlen(buf);
3455   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3456   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3457   ok(strcmp(buf, TestItem3) == 0,
3458         "streamed text different from, got %s\n", buf);
3459   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3460   p = buf;
3461   es.dwCookie = (DWORD_PTR)&p;
3462   es.dwError = 0;
3463   es.pfnCallback = test_WM_SETTEXT_esCallback;
3464   memset(buf, 0, sizeof(buf));
3465   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3466   r = strlen(buf);
3467   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3468   ok(strcmp(buf, TestItem3) == 0,
3469         "streamed text different, got %s\n", buf);
3470
3471   DestroyWindow(hwndRichEdit);
3472 }
3473
3474 static void test_EM_STREAMOUT_FONTTBL(void)
3475 {
3476   HWND hwndRichEdit = new_richedit(NULL);
3477   EDITSTREAM es;
3478   char buf[1024] = {0};
3479   char * p;
3480   char * fontTbl;
3481   int brackCount;
3482
3483   const char * TestItem = "TestSomeText";
3484
3485   /* fills in the richedit control with some text */
3486   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3487
3488   /* streams out the text in rtf format */
3489   p = buf;
3490   es.dwCookie = (DWORD_PTR)&p;
3491   es.dwError = 0;
3492   es.pfnCallback = test_WM_SETTEXT_esCallback;
3493   memset(buf, 0, sizeof(buf));
3494   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3495
3496   /* scans for \fonttbl, error if not found */
3497   fontTbl = strstr(buf, "\\fonttbl");
3498   ok(fontTbl != NULL, "missing \\fonttbl section\n");
3499   if(fontTbl)
3500   {
3501       /* scans for terminating closing bracket */
3502       brackCount = 1;
3503       while(*fontTbl && brackCount)
3504       {
3505           if(*fontTbl == '{')
3506               brackCount++;
3507           else if(*fontTbl == '}')
3508               brackCount--;
3509           fontTbl++;
3510       }
3511     /* checks whether closing bracket is ok */
3512       ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3513       if(!brackCount)
3514       {
3515           /* char before closing fonttbl block should be a closed bracket */
3516           fontTbl -= 2;
3517           ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3518
3519           /* char after fonttbl block should be a crlf */
3520           fontTbl += 2;
3521           ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3522       }
3523   }
3524   DestroyWindow(hwndRichEdit);
3525 }
3526
3527
3528 static void test_EM_SETTEXTEX(void)
3529 {
3530   HWND hwndRichEdit, parent;
3531   SCROLLINFO si;
3532   int sel_start, sel_end;
3533   SETTEXTEX setText;
3534   GETTEXTEX getText;
3535   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
3536                        'S', 'o', 'm', 'e', 
3537                        'T', 'e', 'x', 't', 0}; 
3538   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3539                           't', 'S', 'o', 'm',
3540                           'e', 'T', 'e', 'x',
3541                           't', 't', 'S', 'o',
3542                           'm', 'e', 'T', 'e',
3543                           'x', 't', 0};
3544   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3545                            '\r','t','S','o','m','e','T','e','x','t',0};
3546   WCHAR TestItem2[] = {'T', 'e', 's', 't',
3547                        'S', 'o', 'm', 'e',
3548                        'T', 'e', 'x', 't',
3549                       '\r', 0};
3550   const char * TestItem2_after = "TestSomeText\r\n";
3551   WCHAR TestItem3[] = {'T', 'e', 's', 't',
3552                        'S', 'o', 'm', 'e',
3553                        'T', 'e', 'x', 't',
3554                       '\r','\n','\r','\n', 0};
3555   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3556                        'S', 'o', 'm', 'e',
3557                        'T', 'e', 'x', 't',
3558                        '\n','\n', 0};
3559   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3560                        'S', 'o', 'm', 'e',
3561                        'T', 'e', 'x', 't',
3562                        '\r','\r', 0};
3563   WCHAR TestItem4[] = {'T', 'e', 's', 't',
3564                        'S', 'o', 'm', 'e',
3565                        'T', 'e', 'x', 't',
3566                       '\r','\r','\n','\r',
3567                       '\n', 0};
3568   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3569                        'S', 'o', 'm', 'e',
3570                        'T', 'e', 'x', 't',
3571                        ' ','\r', 0};
3572 #define MAX_BUF_LEN 1024
3573   WCHAR buf[MAX_BUF_LEN];
3574   char bufACP[MAX_BUF_LEN];
3575   char * p;
3576   int result;
3577   CHARRANGE cr;
3578   EDITSTREAM es;
3579   WNDCLASSA cls;
3580
3581   /* Test the scroll position with and without a parent window.
3582    *
3583    * For some reason the scroll position is 0 after EM_SETTEXTEX
3584    * with the ST_SELECTION flag only when the control has a parent
3585    * window, even though the selection is at the end. */
3586   cls.style = 0;
3587   cls.lpfnWndProc = DefWindowProcA;
3588   cls.cbClsExtra = 0;
3589   cls.cbWndExtra = 0;
3590   cls.hInstance = GetModuleHandleA(0);
3591   cls.hIcon = 0;
3592   cls.hCursor = LoadCursorA(0, IDC_ARROW);
3593   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3594   cls.lpszMenuName = NULL;
3595   cls.lpszClassName = "ParentTestClass";
3596   if(!RegisterClassA(&cls)) assert(0);
3597
3598   parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3599                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
3600   ok (parent != 0, "Failed to create parent window\n");
3601
3602   hwndRichEdit = CreateWindowEx(0,
3603                         RICHEDIT_CLASS, NULL,
3604                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3605                         0, 0, 200, 60, parent, NULL,
3606                         hmoduleRichEdit, NULL);
3607
3608   setText.codepage = CP_ACP;
3609   setText.flags = ST_SELECTION;
3610   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3611               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3612   si.cbSize = sizeof(si);
3613   si.fMask = SIF_ALL;
3614   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3615   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3616   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3617   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3618   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3619
3620   DestroyWindow(parent);
3621
3622   /* Test without a parent window */
3623   hwndRichEdit = new_richedit(NULL);
3624   setText.codepage = CP_ACP;
3625   setText.flags = ST_SELECTION;
3626   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3627               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3628   si.cbSize = sizeof(si);
3629   si.fMask = SIF_ALL;
3630   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3631   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3632   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3633   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3634   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3635
3636   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3637    * but this time it is because the selection is at the beginning. */
3638   setText.codepage = CP_ACP;
3639   setText.flags = ST_DEFAULT;
3640   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3641               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3642   si.cbSize = sizeof(si);
3643   si.fMask = SIF_ALL;
3644   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3645   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3646   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3647   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3648   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3649
3650   setText.codepage = 1200;  /* no constant for unicode */
3651   getText.codepage = 1200;  /* no constant for unicode */
3652   getText.cb = MAX_BUF_LEN;
3653   getText.flags = GT_DEFAULT;
3654   getText.lpDefaultChar = NULL;
3655   getText.lpUsedDefChar = NULL;
3656
3657   setText.flags = 0;
3658   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3659   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3660   ok(lstrcmpW(buf, TestItem1) == 0,
3661       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3662
3663   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3664      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3665    */
3666   setText.codepage = 1200;  /* no constant for unicode */
3667   getText.codepage = 1200;  /* no constant for unicode */
3668   getText.cb = MAX_BUF_LEN;
3669   getText.flags = GT_DEFAULT;
3670   getText.lpDefaultChar = NULL;
3671   getText.lpUsedDefChar = NULL;
3672   setText.flags = 0;
3673   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3674   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3675   ok(lstrcmpW(buf, TestItem2) == 0,
3676       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3677
3678   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3679   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3680   ok(strcmp((const char *)buf, TestItem2_after) == 0,
3681       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3682
3683   /* Baseline test for just-enough buffer space for string */
3684   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3685   getText.codepage = 1200;  /* no constant for unicode */
3686   getText.flags = GT_DEFAULT;
3687   getText.lpDefaultChar = NULL;
3688   getText.lpUsedDefChar = NULL;
3689   memset(buf, 0, MAX_BUF_LEN);
3690   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3691   ok(lstrcmpW(buf, TestItem2) == 0,
3692       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3693
3694   /* When there is enough space for one character, but not both, of the CRLF
3695      pair at the end of the string, the CR is not copied at all. That is,
3696      the caller must not see CRLF pairs truncated to CR at the end of the
3697      string.
3698    */
3699   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3700   getText.codepage = 1200;  /* no constant for unicode */
3701   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
3702   getText.lpDefaultChar = NULL;
3703   getText.lpUsedDefChar = NULL;
3704   memset(buf, 0, MAX_BUF_LEN);
3705   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3706   ok(lstrcmpW(buf, TestItem1) == 0,
3707       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3708
3709
3710   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3711   setText.codepage = 1200;  /* no constant for unicode */
3712   getText.codepage = 1200;  /* no constant for unicode */
3713   getText.cb = MAX_BUF_LEN;
3714   getText.flags = GT_DEFAULT;
3715   getText.lpDefaultChar = NULL;
3716   getText.lpUsedDefChar = NULL;
3717   setText.flags = 0;
3718   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3719   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3720   ok(lstrcmpW(buf, TestItem3_after) == 0,
3721       "EM_SETTEXTEX did not convert properly\n");
3722
3723   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3724   setText.codepage = 1200;  /* no constant for unicode */
3725   getText.codepage = 1200;  /* no constant for unicode */
3726   getText.cb = MAX_BUF_LEN;
3727   getText.flags = GT_DEFAULT;
3728   getText.lpDefaultChar = NULL;
3729   getText.lpUsedDefChar = NULL;
3730   setText.flags = 0;
3731   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3732   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3733   ok(lstrcmpW(buf, TestItem3_after) == 0,
3734       "EM_SETTEXTEX did not convert properly\n");
3735
3736   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3737   setText.codepage = 1200;  /* no constant for unicode */
3738   getText.codepage = 1200;  /* no constant for unicode */
3739   getText.cb = MAX_BUF_LEN;
3740   getText.flags = GT_DEFAULT;
3741   getText.lpDefaultChar = NULL;
3742   getText.lpUsedDefChar = NULL;
3743   setText.flags = 0;
3744   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3745   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3746   ok(lstrcmpW(buf, TestItem4_after) == 0,
3747       "EM_SETTEXTEX did not convert properly\n");
3748
3749   /* !ST_SELECTION && Unicode && !\rtf */
3750   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3751   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3752   
3753   ok (result == 1, 
3754       "EM_SETTEXTEX returned %d, instead of 1\n",result);
3755   ok(lstrlenW(buf) == 0,
3756       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3757   
3758   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3759   setText.flags = 0;
3760   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3761   /* select some text */
3762   cr.cpMax = 1;
3763   cr.cpMin = 3;
3764   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3765   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3766   setText.flags = ST_SELECTION;
3767   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3768   ok(result == 0,
3769       "EM_SETTEXTEX with NULL lParam to replace selection"
3770       " with no text should return 0. Got %i\n",
3771       result);
3772   
3773   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3774   setText.flags = 0;
3775   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3776   /* select some text */
3777   cr.cpMax = 1;
3778   cr.cpMin = 3;
3779   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3780   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3781   setText.flags = ST_SELECTION;
3782   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3783                        (WPARAM)&setText, (LPARAM) TestItem1);
3784   /* get text */
3785   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3786   ok(result == lstrlenW(TestItem1),
3787       "EM_SETTEXTEX with NULL lParam to replace selection"
3788       " with no text should return 0. Got %i\n",
3789       result);
3790   ok(lstrlenW(buf) == 22,
3791       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3792       lstrlenW(buf) );
3793
3794   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3795   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3796   p = (char *)buf;
3797   es.dwCookie = (DWORD_PTR)&p;
3798   es.dwError = 0;
3799   es.pfnCallback = test_WM_SETTEXT_esCallback;
3800   memset(buf, 0, sizeof(buf));
3801   SendMessage(hwndRichEdit, EM_STREAMOUT,
3802               (WPARAM)(SF_RTF), (LPARAM)&es);
3803   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3804
3805   /* !ST_SELECTION && !Unicode && \rtf */
3806   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3807   getText.codepage = 1200;  /* no constant for unicode */
3808   getText.cb = MAX_BUF_LEN;
3809   getText.flags = GT_DEFAULT;
3810   getText.lpDefaultChar = NULL;
3811   getText.lpUsedDefChar = NULL;
3812
3813   setText.flags = 0;
3814   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3815   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3816   ok(lstrcmpW(buf, TestItem1) == 0,
3817       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3818
3819   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3820    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3821   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3822   getText.codepage = CP_ACP;
3823   getText.cb = MAX_BUF_LEN;
3824   getText.flags = GT_DEFAULT;
3825   getText.lpDefaultChar = NULL;
3826   getText.lpUsedDefChar = NULL;
3827
3828   setText.flags = ST_SELECTION;
3829   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3830   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3831   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3832   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3833   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3834
3835   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3836   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3837   p = (char *)buf;
3838   es.dwCookie = (DWORD_PTR)&p;
3839   es.dwError = 0;
3840   es.pfnCallback = test_WM_SETTEXT_esCallback;
3841   memset(buf, 0, sizeof(buf));
3842   SendMessage(hwndRichEdit, EM_STREAMOUT,
3843               (WPARAM)(SF_RTF), (LPARAM)&es);
3844   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3845
3846   /* select some text */
3847   cr.cpMax = 1;
3848   cr.cpMin = 3;
3849   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3850
3851   /* ST_SELECTION && !Unicode && \rtf */
3852   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3853   getText.codepage = 1200;  /* no constant for unicode */
3854   getText.cb = MAX_BUF_LEN;
3855   getText.flags = GT_DEFAULT;
3856   getText.lpDefaultChar = NULL;
3857   getText.lpUsedDefChar = NULL;
3858
3859   setText.flags = ST_SELECTION;
3860   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3861   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3862   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3863
3864   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3865   setText.codepage = 1200;  /* no constant for unicode */
3866   getText.codepage = CP_ACP;
3867   getText.cb = MAX_BUF_LEN;
3868
3869   setText.flags = 0;
3870   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3871   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3872
3873   /* select some text */
3874   cr.cpMax = 1;
3875   cr.cpMin = 3;
3876   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3877
3878   /* ST_SELECTION && !Unicode && !\rtf */
3879   setText.codepage = CP_ACP;
3880   getText.codepage = 1200;  /* no constant for unicode */
3881   getText.cb = MAX_BUF_LEN;
3882   getText.flags = GT_DEFAULT;
3883   getText.lpDefaultChar = NULL;
3884   getText.lpUsedDefChar = NULL;
3885
3886   setText.flags = ST_SELECTION;
3887   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3888   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3889   ok(lstrcmpW(buf, TestItem1alt) == 0,
3890       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3891       " using ST_SELECTION and non-Unicode\n");
3892
3893   /* Test setting text using rich text format */
3894   setText.flags = 0;
3895   setText.codepage = CP_ACP;
3896   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3897   getText.codepage = CP_ACP;
3898   getText.cb = MAX_BUF_LEN;
3899   getText.flags = GT_DEFAULT;
3900   getText.lpDefaultChar = NULL;
3901   getText.lpUsedDefChar = NULL;
3902   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3903   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3904
3905   setText.flags = 0;
3906   setText.codepage = CP_ACP;
3907   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3908   getText.codepage = CP_ACP;
3909   getText.cb = MAX_BUF_LEN;
3910   getText.flags = GT_DEFAULT;
3911   getText.lpDefaultChar = NULL;
3912   getText.lpUsedDefChar = NULL;
3913   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3914   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3915
3916   DestroyWindow(hwndRichEdit);
3917 }
3918
3919 static void test_EM_LIMITTEXT(void)
3920 {
3921   int ret;
3922
3923   HWND hwndRichEdit = new_richedit(NULL);
3924
3925   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3926    * about setting the length to -1 for multiline edit controls doesn't happen.
3927    */
3928
3929   /* Don't check default gettextlimit case. That's done in other tests */
3930
3931   /* Set textlimit to 100 */
3932   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3933   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3934   ok (ret == 100,
3935       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3936
3937   /* Set textlimit to 0 */
3938   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3939   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3940   ok (ret == 65536,
3941       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3942
3943   /* Set textlimit to -1 */
3944   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3945   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3946   ok (ret == -1,
3947       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3948
3949   /* Set textlimit to -2 */
3950   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3951   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3952   ok (ret == -2,
3953       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3954
3955   DestroyWindow (hwndRichEdit);
3956 }
3957
3958
3959 static void test_EM_EXLIMITTEXT(void)
3960 {
3961   int i, selBegin, selEnd, len1, len2;
3962   int result;
3963   char text[1024 + 1];
3964   char buffer[1024 + 1];
3965   int textlimit = 0; /* multiple of 100 */
3966   HWND hwndRichEdit = new_richedit(NULL);
3967   
3968   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3969   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3970   
3971   textlimit = 256000;
3972   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3973   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3974   /* set higher */
3975   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3976   
3977   textlimit = 1000;
3978   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3979   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3980   /* set lower */
3981   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3982  
3983   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3984   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3985   /* default for WParam = 0 */
3986   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3987  
3988   textlimit = sizeof(text)-1;
3989   memset(text, 'W', textlimit);
3990   text[sizeof(text)-1] = 0;
3991   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3992   /* maxed out text */
3993   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3994   
3995   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
3996   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3997   len1 = selEnd - selBegin;
3998   
3999   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4000   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4001   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4002   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4003   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4004   len2 = selEnd - selBegin;
4005   
4006   ok(len1 != len2,
4007     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4008     len1,len2,i);
4009   
4010   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4011   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4012   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
4013   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4014   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4015   len1 = selEnd - selBegin;
4016   
4017   ok(len1 != len2,
4018     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4019     len1,len2,i);
4020   
4021   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4022   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4023   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
4024   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4025   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4026   len2 = selEnd - selBegin;
4027   
4028   ok(len1 == len2, 
4029     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4030     len1,len2,i);
4031
4032   /* set text up to the limit, select all the text, then add a char */
4033   textlimit = 5;
4034   memset(text, 'W', textlimit);
4035   text[textlimit] = 0;
4036   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4037   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4038   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4039   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4040   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4041   result = strcmp(buffer, "A");
4042   ok(0 == result, "got string = \"%s\"\n", buffer);
4043
4044   /* WM_SETTEXT not limited */
4045   textlimit = 10;
4046   memset(text, 'W', textlimit);
4047   text[textlimit] = 0;
4048   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4049   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4050   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4051   i = strlen(buffer);
4052   ok(10 == i, "expected 10 chars\n");
4053   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4054   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4055
4056   /* try inserting more text at end */
4057   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4058   ok(0 == i, "WM_CHAR wasn't processed\n");
4059   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4060   i = strlen(buffer);
4061   ok(10 == i, "expected 10 chars, got %i\n", i);
4062   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4063   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4064
4065   /* try inserting text at beginning */
4066   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4067   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4068   ok(0 == i, "WM_CHAR wasn't processed\n");
4069   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4070   i = strlen(buffer);
4071   ok(10 == i, "expected 10 chars, got %i\n", i);
4072   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4073   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4074
4075   /* WM_CHAR is limited */
4076   textlimit = 1;
4077   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4078   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4079   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4080   ok(0 == i, "WM_CHAR wasn't processed\n");
4081   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4082   ok(0 == i, "WM_CHAR wasn't processed\n");
4083   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4084   i = strlen(buffer);
4085   ok(1 == i, "expected 1 chars, got %i instead\n", i);
4086
4087   DestroyWindow(hwndRichEdit);
4088 }
4089
4090 static void test_EM_GETLIMITTEXT(void)
4091 {
4092   int i;
4093   HWND hwndRichEdit = new_richedit(NULL);
4094
4095   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4096   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4097
4098   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4099   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4100   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4101
4102   DestroyWindow(hwndRichEdit);
4103 }
4104
4105 static void test_WM_SETFONT(void)
4106 {
4107   /* There is no invalid input or error conditions for this function.
4108    * NULL wParam and lParam just fall back to their default values 
4109    * It should be noted that even if you use a gibberish name for your fonts
4110    * here, it will still work because the name is stored. They will display as
4111    * System, but will report their name to be whatever they were created as */
4112   
4113   HWND hwndRichEdit = new_richedit(NULL);
4114   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4115     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4116     FF_DONTCARE, "Marlett");
4117   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4118     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4119     FF_DONTCARE, "MS Sans Serif");
4120   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4121     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4122     FF_DONTCARE, "Courier");
4123   LOGFONTA sentLogFont;
4124   CHARFORMAT2A returnedCF2A;
4125   
4126   returnedCF2A.cbSize = sizeof(returnedCF2A);
4127   
4128   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4129   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4130   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4131
4132   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4133   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4134     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4135     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4136
4137   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4138   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4139   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4140   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4141     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4142     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4143     
4144   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4145   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4146   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4147   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4148     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4149     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4150    
4151   /* This last test is special since we send in NULL. We clear the variables
4152    * and just compare to "System" instead of the sent in font name. */
4153   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4154   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4155   returnedCF2A.cbSize = sizeof(returnedCF2A);
4156   
4157   SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4158   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4159   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4160   ok (!strcmp("System",returnedCF2A.szFaceName),
4161     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4162   
4163   DestroyWindow(hwndRichEdit);
4164 }
4165
4166
4167 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4168                                          LPBYTE pbBuff,
4169                                          LONG cb,
4170                                          LONG *pcb)
4171 {
4172   const char** str = (const char**)dwCookie;
4173   int size = strlen(*str);
4174   if(size > 3)  /* let's make it piecemeal for fun */
4175     size = 3;
4176   *pcb = cb;
4177   if (*pcb > size) {
4178     *pcb = size;
4179   }
4180   if (*pcb > 0) {
4181     memcpy(pbBuff, *str, *pcb);
4182     *str += *pcb;
4183   }
4184   return 0;
4185 }
4186
4187 static void test_EM_GETMODIFY(void)
4188 {
4189   HWND hwndRichEdit = new_richedit(NULL);
4190   LRESULT result;
4191   SETTEXTEX setText;
4192   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
4193                        'S', 'o', 'm', 'e', 
4194                        'T', 'e', 'x', 't', 0}; 
4195   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
4196                        'S', 'o', 'm', 'e', 
4197                        'O', 't', 'h', 'e', 'r',
4198                        'T', 'e', 'x', 't', 0}; 
4199   const char* streamText = "hello world";
4200   CHARFORMAT2 cf2;
4201   PARAFORMAT2 pf2;
4202   EDITSTREAM es;
4203   
4204   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4205     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4206     FF_DONTCARE, "Courier");
4207   
4208   setText.codepage = 1200;  /* no constant for unicode */
4209   setText.flags = ST_KEEPUNDO;
4210   
4211
4212   /* modify flag shouldn't be set when richedit is first created */
4213   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4214   ok (result == 0, 
4215       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4216   
4217   /* setting modify flag should actually set it */
4218   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4219   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4220   ok (result != 0, 
4221       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4222   
4223   /* clearing modify flag should actually clear it */
4224   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4225   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226   ok (result == 0, 
4227       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4228  
4229   /* setting font doesn't change modify flag */
4230   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4231   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4232   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4233   ok (result == 0,
4234       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4235
4236   /* setting text should set modify flag */
4237   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4238   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4239   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4240   ok (result != 0,
4241       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4242   
4243   /* undo previous text doesn't reset modify flag */
4244   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4245   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4246   ok (result != 0,
4247       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4248   
4249   /* set text with no flag to keep undo stack should not set modify flag */
4250   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4251   setText.flags = 0;
4252   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4253   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4254   ok (result == 0,
4255       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4256   
4257   /* WM_SETTEXT doesn't modify */
4258   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4259   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4260   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4261   ok (result == 0,
4262       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4263   
4264   /* clear the text */
4265   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4266   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4267   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4268   ok (result == 0,
4269       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4270   
4271   /* replace text */
4272   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4273   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4274   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4275   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4276   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277   ok (result != 0,
4278       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4279   
4280   /* copy/paste text 1 */
4281   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4282   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4283   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4284   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4285   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4286   ok (result != 0,
4287       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4288   
4289   /* copy/paste text 2 */
4290   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4291   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4292   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4293   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4294   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4295   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4296   ok (result != 0,
4297       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4298   
4299   /* press char */
4300   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4301   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4302   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4303   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4304   ok (result != 0,
4305       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4306
4307   /* press del */
4308   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4309   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4310   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4311   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4312   ok (result != 0,
4313       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4314   
4315   /* set char format */
4316   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4317   cf2.cbSize = sizeof(CHARFORMAT2);
4318   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4319   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4320   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4321   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4322   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4323   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4324   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4325   ok (result != 0,
4326       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4327   
4328   /* set para format */
4329   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4330   pf2.cbSize = sizeof(PARAFORMAT2);
4331   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4332              (LPARAM) &pf2);
4333   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4334   pf2.wAlignment = PFA_RIGHT;
4335   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4336   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4337   ok (result == 0,
4338       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4339
4340   /* EM_STREAM */
4341   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4342   es.dwCookie = (DWORD_PTR)&streamText;
4343   es.dwError = 0;
4344   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4345   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4346   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4347   ok (result != 0,
4348       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4349
4350   DestroyWindow(hwndRichEdit);
4351 }
4352
4353 struct exsetsel_s {
4354   LONG min;
4355   LONG max;
4356   LRESULT expected_retval;
4357   int expected_getsel_start;
4358   int expected_getsel_end;
4359   int _getsel_todo_wine;
4360 };
4361
4362 const struct exsetsel_s exsetsel_tests[] = {
4363   /* sanity tests */
4364   {5, 10, 10, 5, 10, 0},
4365   {15, 17, 17, 15, 17, 0},
4366   /* test cpMax > strlen() */
4367   {0, 100, 18, 0, 18, 1},
4368   /* test cpMin == cpMax */
4369   {5, 5, 5, 5, 5, 0},
4370   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4371   {-1, 0, 5, 5, 5, 0},
4372   {-1, 17, 5, 5, 5, 0},
4373   {-1, 18, 5, 5, 5, 0},
4374   /* test cpMin < 0 && cpMax < 0 */
4375   {-1, -1, 17, 17, 17, 0},
4376   {-4, -5, 17, 17, 17, 0},
4377   /* test cMin >=0 && cpMax < 0 (bug 6814) */
4378   {0, -1, 18, 0, 18, 1},
4379   {17, -5, 18, 17, 18, 1},
4380   {18, -3, 17, 17, 17, 0},
4381   /* test if cpMin > cpMax */
4382   {15, 19, 18, 15, 18, 1},
4383   {19, 15, 18, 15, 18, 1}
4384 };
4385
4386 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4387     CHARRANGE cr;
4388     LRESULT result;
4389     int start, end;
4390
4391     cr.cpMin = setsel->min;
4392     cr.cpMax = setsel->max;
4393     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4394
4395     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4396
4397     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4398
4399     if (setsel->_getsel_todo_wine) {
4400         todo_wine {
4401             ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4402         }
4403     } else {
4404         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4405     }
4406 }
4407
4408 static void test_EM_EXSETSEL(void)
4409 {
4410     HWND hwndRichEdit = new_richedit(NULL);
4411     int i;
4412     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4413
4414     /* sending some text to the window */
4415     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4416     /*                                                 01234567890123456*/
4417     /*                                                          10      */
4418
4419     for (i = 0; i < num_tests; i++) {
4420         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4421     }
4422
4423     DestroyWindow(hwndRichEdit);
4424 }
4425
4426 static void test_EM_REPLACESEL(int redraw)
4427 {
4428     HWND hwndRichEdit = new_richedit(NULL);
4429     char buffer[1024] = {0};
4430     int r;
4431     GETTEXTEX getText;
4432     CHARRANGE cr;
4433
4434     /* sending some text to the window */
4435     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4436     /*                                                 01234567890123456*/
4437     /*                                                          10      */
4438
4439     /* FIXME add more tests */
4440     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4441     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4442     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4443     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4444     r = strcmp(buffer, "testing");
4445     ok(0 == r, "expected %d, got %d\n", 0, r);
4446
4447     DestroyWindow(hwndRichEdit);
4448
4449     hwndRichEdit = new_richedit(NULL);
4450
4451     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4452     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4453
4454     /* Test behavior with carriage returns and newlines */
4455     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4456     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4457     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4458     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4459     r = strcmp(buffer, "RichEdit1");
4460     ok(0 == r, "expected %d, got %d\n", 0, r);
4461     getText.cb = 1024;
4462     getText.codepage = CP_ACP;
4463     getText.flags = GT_DEFAULT;
4464     getText.lpDefaultChar = NULL;
4465     getText.lpUsedDefChar = NULL;
4466     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4467     ok(strcmp(buffer, "RichEdit1") == 0,
4468       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4469
4470     /* Test number of lines reported after EM_REPLACESEL */
4471     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4472     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4473
4474     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4475     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4476     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4477     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4478     r = strcmp(buffer, "RichEdit1\r\n");
4479     ok(0 == r, "expected %d, got %d\n", 0, r);
4480     getText.cb = 1024;
4481     getText.codepage = CP_ACP;
4482     getText.flags = GT_DEFAULT;
4483     getText.lpDefaultChar = NULL;
4484     getText.lpUsedDefChar = NULL;
4485     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4486     ok(strcmp(buffer, "RichEdit1\r") == 0,
4487       "EM_GETTEXTEX returned incorrect string\n");
4488
4489     /* Test number of lines reported after EM_REPLACESEL */
4490     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4491     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4492
4493     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4494     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4495     ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4496
4497     /* Test number of lines reported after EM_REPLACESEL */
4498     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4499     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4500
4501     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4502     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4503     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4504     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4505
4506     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4507     r = strcmp(buffer, "RichEdit1\r\n");
4508     ok(0 == r, "expected %d, got %d\n", 0, r);
4509     getText.cb = 1024;
4510     getText.codepage = CP_ACP;
4511     getText.flags = GT_DEFAULT;
4512     getText.lpDefaultChar = NULL;
4513     getText.lpUsedDefChar = NULL;
4514     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4515     ok(strcmp(buffer, "RichEdit1\r") == 0,
4516       "EM_GETTEXTEX returned incorrect string\n");
4517
4518     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4519     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4520     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4521     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4522
4523     /* The following tests show that richedit should handle the special \r\r\n
4524        sequence by turning it into a single space on insertion. However,
4525        EM_REPLACESEL on WinXP returns the number of characters in the original
4526        string.
4527      */
4528
4529     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4530     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4531     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4532     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4533     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4534     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4535     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4536
4537     /* Test the actual string */
4538     getText.cb = 1024;
4539     getText.codepage = CP_ACP;
4540     getText.flags = GT_DEFAULT;
4541     getText.lpDefaultChar = NULL;
4542     getText.lpUsedDefChar = NULL;
4543     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4544     ok(strcmp(buffer, "\r\r") == 0,
4545       "EM_GETTEXTEX returned incorrect string\n");
4546
4547     /* Test number of lines reported after EM_REPLACESEL */
4548     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4549     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4550
4551     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4552     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4553     ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4554     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4555     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4556     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4557     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4558
4559     /* Test the actual string */
4560     getText.cb = 1024;
4561     getText.codepage = CP_ACP;
4562     getText.flags = GT_DEFAULT;
4563     getText.lpDefaultChar = NULL;
4564     getText.lpUsedDefChar = NULL;
4565     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4566     ok(strcmp(buffer, " ") == 0,
4567       "EM_GETTEXTEX returned incorrect string\n");
4568
4569     /* Test number of lines reported after EM_REPLACESEL */
4570     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4571     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4572
4573     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4574     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4575     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4576     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4577     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4578     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4579     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4580
4581     /* Test the actual string */
4582     getText.cb = 1024;
4583     getText.codepage = CP_ACP;
4584     getText.flags = GT_DEFAULT;
4585     getText.lpDefaultChar = NULL;
4586     getText.lpUsedDefChar = NULL;
4587     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4588     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4589       "EM_GETTEXTEX returned incorrect string\n");
4590
4591     /* Test number of lines reported after EM_REPLACESEL */
4592     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4593     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4594
4595     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4596     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4597     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4598     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4599     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4600     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4601     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4602
4603     /* Test the actual string */
4604     getText.cb = 1024;
4605     getText.codepage = CP_ACP;
4606     getText.flags = GT_DEFAULT;
4607     getText.lpDefaultChar = NULL;
4608     getText.lpUsedDefChar = NULL;
4609     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4610     ok(strcmp(buffer, " \r") == 0,
4611       "EM_GETTEXTEX returned incorrect string\n");
4612
4613     /* Test number of lines reported after EM_REPLACESEL */
4614     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4615     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4616
4617     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4618     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4619     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4620     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4621     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4622     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4623     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4624
4625     /* Test the actual string */
4626     getText.cb = 1024;
4627     getText.codepage = CP_ACP;
4628     getText.flags = GT_DEFAULT;
4629     getText.lpDefaultChar = NULL;
4630     getText.lpUsedDefChar = NULL;
4631     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4632     ok(strcmp(buffer, " \r\r") == 0,
4633       "EM_GETTEXTEX returned incorrect string\n");
4634
4635     /* Test number of lines reported after EM_REPLACESEL */
4636     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4637     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4638
4639     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4640     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4641     ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4642     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4643     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4644     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4645     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4646
4647     /* Test the actual string */
4648     getText.cb = 1024;
4649     getText.codepage = CP_ACP;
4650     getText.flags = GT_DEFAULT;
4651     getText.lpDefaultChar = NULL;
4652     getText.lpUsedDefChar = NULL;
4653     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4654     ok(strcmp(buffer, "\rX\r\r\r") == 0,
4655       "EM_GETTEXTEX returned incorrect string\n");
4656
4657     /* Test number of lines reported after EM_REPLACESEL */
4658     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4659     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4660
4661     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4662     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4663     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4664     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4665     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4666     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4667     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4668
4669     /* Test the actual string */
4670     getText.cb = 1024;
4671     getText.codepage = CP_ACP;
4672     getText.flags = GT_DEFAULT;
4673     getText.lpDefaultChar = NULL;
4674     getText.lpUsedDefChar = NULL;
4675     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4676     ok(strcmp(buffer, "\r\r") == 0,
4677       "EM_GETTEXTEX returned incorrect string\n");
4678
4679     /* Test number of lines reported after EM_REPLACESEL */
4680     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4681     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4682
4683     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4684     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4685     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4686     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4687     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4688     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4689     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4690
4691     /* Test the actual string */
4692     getText.cb = 1024;
4693     getText.codepage = CP_ACP;
4694     getText.flags = GT_DEFAULT;
4695     getText.lpDefaultChar = NULL;
4696     getText.lpUsedDefChar = NULL;
4697     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4698     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4699       "EM_GETTEXTEX returned incorrect string\n");
4700
4701     /* Test number of lines reported after EM_REPLACESEL */
4702     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4703     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4704
4705     if (!redraw)
4706         /* This is needed to avoid interferring with keybd_event calls
4707          * on other tests that simulate keyboard events. */
4708         SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4709
4710     DestroyWindow(hwndRichEdit);
4711 }
4712
4713 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4714  * to test the state of the modifiers (Ctrl/Alt/Shift).
4715  *
4716  * Therefore Ctrl-<key> keystrokes need to be simulated with
4717  * keybd_event or by using SetKeyboardState to set the modifiers
4718  * and SendMessage to simulate the keystrokes.
4719  */
4720 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4721 {
4722     LRESULT result;
4723     hold_key(VK_CONTROL);
4724     result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4725     release_key(VK_CONTROL);
4726     return result;
4727 }
4728
4729 static void test_WM_PASTE(void)
4730 {
4731     int result;
4732     char buffer[1024] = {0};
4733     const char* text1 = "testing paste\r";
4734     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4735     const char* text1_after = "testing paste\r\n";
4736     const char* text2 = "testing paste\r\rtesting paste";
4737     const char* text2_after = "testing paste\r\n\r\ntesting paste";
4738     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4739     HWND hwndRichEdit = new_richedit(NULL);
4740
4741     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4742     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4743
4744     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
4745     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4746     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
4747     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4748     /* Pasted text should be visible at this step */
4749     result = strcmp(text1_step1, buffer);
4750     ok(result == 0,
4751         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4752
4753     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
4754     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4755     /* Text should be the same as before (except for \r -> \r\n conversion) */
4756     result = strcmp(text1_after, buffer);
4757     ok(result == 0,
4758         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4759
4760     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4761     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4762     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
4763     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4764     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
4765     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4766     /* Pasted text should be visible at this step */
4767     result = strcmp(text3, buffer);
4768     ok(result == 0,
4769         "test paste: strcmp = %i\n", result);
4770     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
4771     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772     /* Text should be the same as before (except for \r -> \r\n conversion) */
4773     result = strcmp(text2_after, buffer);
4774     ok(result == 0,
4775         "test paste: strcmp = %i\n", result);
4776     send_ctrl_key(hwndRichEdit, 'Y');   /* Redo */
4777     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4778     /* Text should revert to post-paste state */
4779     result = strcmp(buffer,text3);
4780     ok(result == 0,
4781         "test paste: strcmp = %i\n", result);
4782
4783     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4784     /* Send WM_CHAR to simulates Ctrl-V */
4785     SendMessage(hwndRichEdit, WM_CHAR, 22,
4786                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4787     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4788     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4789     result = strcmp(buffer,"");
4790     ok(result == 0,
4791         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4792
4793     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4794      * with SetKeyboard state. */
4795
4796     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4797     /* Simulates paste (Ctrl-V) */
4798     hold_key(VK_CONTROL);
4799     SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4800                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4801     release_key(VK_CONTROL);
4802     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4803     result = strcmp(buffer,"paste");
4804     ok(result == 0,
4805         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4806
4807     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4808     SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4809     /* Simulates copy (Ctrl-C) */
4810     hold_key(VK_CONTROL);
4811     SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4812                 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4813     release_key(VK_CONTROL);
4814     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4815     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4816     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4817     result = strcmp(buffer,"testing");
4818     ok(result == 0,
4819         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4820
4821     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4822     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4823     /* Simulates select all (Ctrl-A) */
4824     hold_key(VK_CONTROL);
4825     SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4826                 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4827     /* Simulates select cut (Ctrl-X) */
4828     SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4829                 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4830     release_key(VK_CONTROL);
4831     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4832     result = strcmp(buffer,"");
4833     ok(result == 0,
4834         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4835     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4836     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4837     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4838     result = strcmp(buffer,"cut\r\n");
4839     todo_wine ok(result == 0,
4840         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4841     /* Simulates undo (Ctrl-Z) */
4842     hold_key(VK_CONTROL);
4843     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4844                 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4845     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4846     result = strcmp(buffer,"");
4847     ok(result == 0,
4848         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4849     /* Simulates redo (Ctrl-Y) */
4850     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4851                 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4852     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4853     result = strcmp(buffer,"cut\r\n");
4854     todo_wine ok(result == 0,
4855         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4856     release_key(VK_CONTROL);
4857
4858     DestroyWindow(hwndRichEdit);
4859 }
4860
4861 static void test_EM_FORMATRANGE(void)
4862 {
4863   int r, i, tpp_x, tpp_y;
4864   HDC hdc;
4865   HWND hwndRichEdit = new_richedit(NULL);
4866   FORMATRANGE fr;
4867   BOOL skip_non_english;
4868   static const struct {
4869     const char *string; /* The string */
4870     int first;          /* First 'pagebreak', 0 for don't care */
4871     int second;         /* Second 'pagebreak', 0 for don't care */
4872   } fmtstrings[] = {
4873     {"WINE wine", 0, 0},
4874     {"WINE wineWine", 0, 0},
4875     {"WINE\r\nwine\r\nwine", 5, 10},
4876     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4877     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4878   };
4879
4880   skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4881   if (skip_non_english)
4882     skip("Skipping some tests on non-English platform\n");
4883
4884   hdc = GetDC(hwndRichEdit);
4885   ok(hdc != NULL, "Could not get HDC\n");
4886
4887   /* Calculate the twips per pixel */
4888   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4889   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4890
4891   /* Test the simple case where all the text fits in the page rect. */
4892   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4893   fr.hdc = fr.hdcTarget = hdc;
4894   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4895   fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4896   fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4897   fr.chrg.cpMin = 0;
4898   fr.chrg.cpMax = -1;
4899   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4900   todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4901
4902   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4903   fr.rc.bottom = fr.rcPage.bottom;
4904   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4905   todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4906
4907   SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4908
4909   for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4910   {
4911     GETTEXTLENGTHEX gtl;
4912     SIZE stringsize;
4913     int len;
4914
4915     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4916
4917     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4918     gtl.codepage = CP_ACP;
4919     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4920
4921     /* Get some size information for the string */
4922     GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4923
4924     /* Define the box to be half the width needed and a bit larger than the height.
4925      * Changes to the width means we have at least 2 pages. Changes to the height
4926      * is done so we can check the changing of fr.rc.bottom.
4927      */
4928     fr.hdc = fr.hdcTarget = hdc;
4929     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4930     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4931     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4932
4933     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4934     todo_wine {
4935     ok(r == len, "Expected %d, got %d\n", len, r);
4936     }
4937
4938     /* We know that the page can't hold the full string. See how many characters
4939      * are on the first one
4940      */
4941     fr.chrg.cpMin = 0;
4942     fr.chrg.cpMax = -1;
4943     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4944     todo_wine {
4945     if (! skip_non_english)
4946       ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4947     }
4948     if (fmtstrings[i].first)
4949       todo_wine {
4950       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4951       }
4952     else
4953       ok(r < len, "Expected < %d, got %d\n", len, r);
4954
4955     /* Do another page */
4956     fr.chrg.cpMin = r;
4957     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4958     if (fmtstrings[i].second)
4959       todo_wine {
4960       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4961       }
4962     else if (! skip_non_english)
4963       ok (r < len, "Expected < %d, got %d\n", len, r);
4964
4965     /* There is at least on more page, but we don't care */
4966
4967     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4968     todo_wine {
4969     ok(r == len, "Expected %d, got %d\n", len, r);
4970     }
4971   }
4972
4973   ReleaseDC(NULL, hdc);
4974   DestroyWindow(hwndRichEdit);
4975 }
4976
4977 static int nCallbackCount = 0;
4978
4979 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4980                                  LONG cb, LONG* pcb)
4981 {
4982   const char text[] = {'t','e','s','t'};
4983
4984   if (sizeof(text) <= cb)
4985   {
4986     if ((int)dwCookie != nCallbackCount)
4987     {
4988       *pcb = 0;
4989       return 0;
4990     }
4991
4992     memcpy (pbBuff, text, sizeof(text));
4993     *pcb = sizeof(text);
4994
4995     nCallbackCount++;
4996
4997     return 0;
4998   }
4999   else
5000     return 1; /* indicates callback failed */
5001 }
5002
5003 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5004                                          LPBYTE pbBuff,
5005                                          LONG cb,
5006                                          LONG *pcb)
5007 {
5008   const char** str = (const char**)dwCookie;
5009   int size = strlen(*str);
5010   *pcb = cb;
5011   if (*pcb > size) {
5012     *pcb = size;
5013   }
5014   if (*pcb > 0) {
5015     memcpy(pbBuff, *str, *pcb);
5016     *str += *pcb;
5017   }
5018   return 0;
5019 }
5020
5021 struct StringWithLength {
5022     int length;
5023     char *buffer;
5024 };
5025
5026 /* This callback is used to handled the null characters in a string. */
5027 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5028                                                    LPBYTE pbBuff,
5029                                                    LONG cb,
5030                                                    LONG *pcb)
5031 {
5032     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5033     int size = str->length;
5034     *pcb = cb;
5035     if (*pcb > size) {
5036       *pcb = size;
5037     }
5038     if (*pcb > 0) {
5039       memcpy(pbBuff, str->buffer, *pcb);
5040       str->buffer += *pcb;
5041       str->length -= *pcb;
5042     }
5043     return 0;
5044 }
5045
5046 static void test_EM_STREAMIN(void)
5047 {
5048   HWND hwndRichEdit = new_richedit(NULL);
5049   LRESULT result;
5050   EDITSTREAM es;
5051   char buffer[1024] = {0};
5052
5053   const char * streamText0 = "{\\rtf1 TestSomeText}";
5054   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5055   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5056
5057   const char * streamText1 =
5058   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5059   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5060   "}\r\n";
5061
5062   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5063   const char * streamText2 =
5064     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5065     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5066     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5067     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5068     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5069     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5070     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5071
5072   const char * streamText3 = "RichEdit1";
5073
5074   const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5075
5076   const char * streamText4 =
5077       "This text just needs to be long enough to cause run to be split onto "
5078       "two separate lines and make sure the null terminating character is "
5079       "handled properly.\0";
5080   int length4 = strlen(streamText4) + 1;
5081   struct StringWithLength cookieForStream4 = {
5082       length4,
5083       (char *)streamText4,
5084   };
5085
5086   const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5087   int length5 = sizeof(streamText5) / sizeof(WCHAR);
5088   struct StringWithLength cookieForStream5 = {
5089       sizeof(streamText5),
5090       (char *)streamText5,
5091   };
5092
5093   /* Minimal test without \par at the end */
5094   es.dwCookie = (DWORD_PTR)&streamText0;
5095   es.dwError = 0;
5096   es.pfnCallback = test_EM_STREAMIN_esCallback;
5097   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5098   ok(result == 12, "got %ld, expected %d\n", result, 12);
5099
5100   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5101   ok (result  == 12,
5102       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5103   result = strcmp (buffer,"TestSomeText");
5104   ok (result  == 0,
5105       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5106   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5107
5108   /* Native richedit 2.0 ignores last \par */
5109   es.dwCookie = (DWORD_PTR)&streamText0a;
5110   es.dwError = 0;
5111   es.pfnCallback = test_EM_STREAMIN_esCallback;
5112   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5113   ok(result == 12, "got %ld, expected %d\n", result, 12);
5114
5115   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5116   ok (result  == 12,
5117       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5118   result = strcmp (buffer,"TestSomeText");
5119   ok (result  == 0,
5120       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5121   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5122
5123   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5124   es.dwCookie = (DWORD_PTR)&streamText0b;
5125   es.dwError = 0;
5126   es.pfnCallback = test_EM_STREAMIN_esCallback;
5127   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5128   ok(result == 13, "got %ld, expected %d\n", result, 13);
5129
5130   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5131   ok (result  == 14,
5132       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5133   result = strcmp (buffer,"TestSomeText\r\n");
5134   ok (result  == 0,
5135       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5136   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5137
5138   es.dwCookie = (DWORD_PTR)&streamText1;
5139   es.dwError = 0;
5140   es.pfnCallback = test_EM_STREAMIN_esCallback;
5141   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5142   ok(result == 12, "got %ld, expected %d\n", result, 12);
5143
5144   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5145   ok (result  == 12,
5146       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5147   result = strcmp (buffer,"TestSomeText");
5148   ok (result  == 0,
5149       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5150   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5151
5152   es.dwCookie = (DWORD_PTR)&streamText2;
5153   es.dwError = 0;
5154   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5155   ok(result == 0, "got %ld, expected %d\n", result, 0);
5156
5157   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5158   ok (result  == 0,
5159       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5160   ok (strlen(buffer)  == 0,
5161       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5162   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5163
5164   es.dwCookie = (DWORD_PTR)&streamText3;
5165   es.dwError = 0;
5166   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5167   ok(result == 0, "got %ld, expected %d\n", result, 0);
5168
5169   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5170   ok (result  == 0,
5171       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5172   ok (strlen(buffer)  == 0,
5173       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5174   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5175
5176   es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5177   es.dwError = 0;
5178   es.pfnCallback = test_EM_STREAMIN_esCallback;
5179   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5180   ok(result == 18, "got %ld, expected %d\n", result, 18);
5181
5182   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5183   ok(result  == 15,
5184       "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5185   result = strcmp (buffer,"TestUTF8WithBOM");
5186   ok(result  == 0,
5187       "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5188   ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5189
5190   es.dwCookie = (DWORD_PTR)&cookieForStream4;
5191   es.dwError = 0;
5192   es.pfnCallback = test_EM_STREAMIN_esCallback2;
5193   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5194   ok(result == length4, "got %ld, expected %d\n", result, length4);
5195
5196   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5197   ok (result  == length4,
5198       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5199   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5200
5201   es.dwCookie = (DWORD_PTR)&cookieForStream5;
5202   es.dwError = 0;
5203   es.pfnCallback = test_EM_STREAMIN_esCallback2;
5204   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5205   ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5206
5207   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5208   ok (result  == length5,
5209       "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5210   ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5211
5212   DestroyWindow(hwndRichEdit);
5213 }
5214
5215 static void test_EM_StreamIn_Undo(void)
5216 {
5217   /* The purpose of this test is to determine when a EM_StreamIn should be
5218    * undoable. This is important because WM_PASTE currently uses StreamIn and
5219    * pasting should always be undoable but streaming isn't always.
5220    *
5221    * cases to test:
5222    * StreamIn plain text without SFF_SELECTION.
5223    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5224    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5225    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5226    * Feel free to add tests for other text modes or StreamIn things.
5227    */
5228
5229
5230   HWND hwndRichEdit = new_richedit(NULL);
5231   LRESULT result;
5232   EDITSTREAM es;
5233   char buffer[1024] = {0};
5234   const char randomtext[] = "Some text";
5235
5236   es.pfnCallback = EditStreamCallback;
5237
5238   /* StreamIn, no SFF_SELECTION */
5239   es.dwCookie = nCallbackCount;
5240   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5241   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5242   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5243   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5244   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5245   result = strcmp (buffer,"test");
5246   ok (result  == 0,
5247       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5248
5249   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5250   ok (result == FALSE,
5251       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5252
5253   /* StreamIn, SFF_SELECTION, but nothing selected */
5254   es.dwCookie = nCallbackCount;
5255   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5256   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5257   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5258   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5259   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5260   result = strcmp (buffer,"testSome text");
5261   ok (result  == 0,
5262       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5263
5264   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5265   ok (result == TRUE,
5266      "EM_STREAMIN with SFF_SELECTION but no selection set "
5267       "should create an undo\n");
5268
5269   /* StreamIn, SFF_SELECTION, with a selection */
5270   es.dwCookie = nCallbackCount;
5271   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5272   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5273   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5274   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5275   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5276   result = strcmp (buffer,"Sometesttext");
5277   ok (result  == 0,
5278       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5279
5280   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5281   ok (result == TRUE,
5282       "EM_STREAMIN with SFF_SELECTION and selection set "
5283       "should create an undo\n");
5284
5285   DestroyWindow(hwndRichEdit);
5286 }
5287
5288 static BOOL is_em_settextex_supported(HWND hwnd)
5289 {
5290     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5291     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5292 }
5293
5294 static void test_unicode_conversions(void)
5295 {
5296     static const WCHAR tW[] = {'t',0};
5297     static const WCHAR teW[] = {'t','e',0};
5298     static const WCHAR textW[] = {'t','e','s','t',0};
5299     static const char textA[] = "test";
5300     char bufA[64];
5301     WCHAR bufW[64];
5302     HWND hwnd;
5303     int em_settextex_supported, ret;
5304
5305 #define set_textA(hwnd, wm_set_text, txt) \
5306     do { \
5307         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5308         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5309         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5310         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5311         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5312     } while(0)
5313 #define expect_textA(hwnd, wm_get_text, txt) \
5314     do { \
5315         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5316         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5317         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5318         memset(bufA, 0xAA, sizeof(bufA)); \
5319         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5320         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5321         ret = lstrcmpA(bufA, txt); \
5322         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5323     } while(0)
5324
5325 #define set_textW(hwnd, wm_set_text, txt) \
5326     do { \
5327         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5328         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5329         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5330         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5331         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5332     } while(0)
5333 #define expect_textW(hwnd, wm_get_text, txt) \
5334     do { \
5335         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5336         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5337         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5338         memset(bufW, 0xAA, sizeof(bufW)); \
5339         ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5340         ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5341         ret = lstrcmpW(bufW, txt); \
5342         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5343     } while(0)
5344 #define expect_empty(hwnd, wm_get_text) \
5345     do { \
5346         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5347         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5348         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5349         memset(bufA, 0xAA, sizeof(bufA)); \
5350         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5351         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5352         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5353     } while(0)
5354
5355     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5356                            0, 0, 200, 60, 0, 0, 0, 0);
5357     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5358
5359     ret = IsWindowUnicode(hwnd);
5360     ok(ret, "RichEdit20W should be unicode under NT\n");
5361
5362     /* EM_SETTEXTEX is supported starting from version 3.0 */
5363     em_settextex_supported = is_em_settextex_supported(hwnd);
5364     trace("EM_SETTEXTEX is %ssupported on this platform\n",
5365           em_settextex_supported ? "" : "NOT ");
5366
5367     expect_empty(hwnd, WM_GETTEXT);
5368     expect_empty(hwnd, EM_GETTEXTEX);
5369
5370     ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5371     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5372     expect_textA(hwnd, WM_GETTEXT, "t");
5373     expect_textA(hwnd, EM_GETTEXTEX, "t");
5374     expect_textW(hwnd, EM_GETTEXTEX, tW);
5375
5376     ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5377     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5378     expect_textA(hwnd, WM_GETTEXT, "te");
5379     expect_textA(hwnd, EM_GETTEXTEX, "te");
5380     expect_textW(hwnd, EM_GETTEXTEX, teW);
5381
5382     set_textA(hwnd, WM_SETTEXT, NULL);
5383     expect_empty(hwnd, WM_GETTEXT);
5384     expect_empty(hwnd, EM_GETTEXTEX);
5385
5386     set_textA(hwnd, WM_SETTEXT, textA);
5387     expect_textA(hwnd, WM_GETTEXT, textA);
5388     expect_textA(hwnd, EM_GETTEXTEX, textA);
5389     expect_textW(hwnd, EM_GETTEXTEX, textW);
5390
5391     if (em_settextex_supported)
5392     {
5393         set_textA(hwnd, EM_SETTEXTEX, textA);
5394         expect_textA(hwnd, WM_GETTEXT, textA);
5395         expect_textA(hwnd, EM_GETTEXTEX, textA);
5396         expect_textW(hwnd, EM_GETTEXTEX, textW);
5397     }
5398
5399     set_textW(hwnd, WM_SETTEXT, textW);
5400     expect_textW(hwnd, WM_GETTEXT, textW);
5401     expect_textA(hwnd, WM_GETTEXT, textA);
5402     expect_textW(hwnd, EM_GETTEXTEX, textW);
5403     expect_textA(hwnd, EM_GETTEXTEX, textA);
5404
5405     if (em_settextex_supported)
5406     {
5407         set_textW(hwnd, EM_SETTEXTEX, textW);
5408         expect_textW(hwnd, WM_GETTEXT, textW);
5409         expect_textA(hwnd, WM_GETTEXT, textA);
5410         expect_textW(hwnd, EM_GETTEXTEX, textW);
5411         expect_textA(hwnd, EM_GETTEXTEX, textA);
5412     }
5413     DestroyWindow(hwnd);
5414
5415     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5416                            0, 0, 200, 60, 0, 0, 0, 0);
5417     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5418
5419     ret = IsWindowUnicode(hwnd);
5420     ok(!ret, "RichEdit20A should NOT be unicode\n");
5421
5422     set_textA(hwnd, WM_SETTEXT, textA);
5423     expect_textA(hwnd, WM_GETTEXT, textA);
5424     expect_textA(hwnd, EM_GETTEXTEX, textA);
5425     expect_textW(hwnd, EM_GETTEXTEX, textW);
5426
5427     if (em_settextex_supported)
5428     {
5429         set_textA(hwnd, EM_SETTEXTEX, textA);
5430         expect_textA(hwnd, WM_GETTEXT, textA);
5431         expect_textA(hwnd, EM_GETTEXTEX, textA);
5432         expect_textW(hwnd, EM_GETTEXTEX, textW);
5433     }
5434
5435         set_textW(hwnd, WM_SETTEXT, textW);
5436         expect_textW(hwnd, WM_GETTEXT, textW);
5437         expect_textA(hwnd, WM_GETTEXT, textA);
5438         expect_textW(hwnd, EM_GETTEXTEX, textW);
5439         expect_textA(hwnd, EM_GETTEXTEX, textA);
5440
5441     if (em_settextex_supported)
5442     {
5443         set_textW(hwnd, EM_SETTEXTEX, textW);
5444         expect_textW(hwnd, WM_GETTEXT, textW);
5445         expect_textA(hwnd, WM_GETTEXT, textA);
5446         expect_textW(hwnd, EM_GETTEXTEX, textW);
5447         expect_textA(hwnd, EM_GETTEXTEX, textA);
5448     }
5449     DestroyWindow(hwnd);
5450 }
5451
5452 static void test_WM_CHAR(void)
5453 {
5454     HWND hwnd;
5455     int ret;
5456     const char * char_list = "abc\rabc\r";
5457     const char * expected_content_single = "abcabc";
5458     const char * expected_content_multi = "abc\r\nabc\r\n";
5459     char buffer[64] = {0};
5460     const char * p;
5461
5462     /* single-line control must IGNORE carriage returns */
5463     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5464                            0, 0, 200, 60, 0, 0, 0, 0);
5465     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5466
5467     p = char_list;
5468     while (*p != '\0') {
5469         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5470         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5471         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5472         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5473         p++;
5474     }
5475
5476     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5477     ret = strcmp(buffer, expected_content_single);
5478     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5479
5480     DestroyWindow(hwnd);
5481
5482     /* multi-line control inserts CR normally */
5483     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5484                            0, 0, 200, 60, 0, 0, 0, 0);
5485     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5486
5487     p = char_list;
5488     while (*p != '\0') {
5489         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5490         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5491         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5492         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5493         p++;
5494     }
5495
5496     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5497     ret = strcmp(buffer, expected_content_multi);
5498     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5499
5500     DestroyWindow(hwnd);
5501 }
5502
5503 static void test_EM_GETTEXTLENGTHEX(void)
5504 {
5505     HWND hwnd;
5506     GETTEXTLENGTHEX gtl;
5507     int ret;
5508     const char * base_string = "base string";
5509     const char * test_string = "a\nb\n\n\r\n";
5510     const char * test_string_after = "a";
5511     const char * test_string_2 = "a\rtest\rstring";
5512     char buffer[64] = {0};
5513
5514     /* single line */
5515     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5516                            0, 0, 200, 60, 0, 0, 0, 0);
5517     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5518
5519     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5520     gtl.codepage = CP_ACP;
5521     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5522     ok(ret == 0, "ret %d\n",ret);
5523
5524     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5525     gtl.codepage = CP_ACP;
5526     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5527     ok(ret == 0, "ret %d\n",ret);
5528
5529     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5530
5531     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5532     gtl.codepage = CP_ACP;
5533     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5534     ok(ret == strlen(base_string), "ret %d\n",ret);
5535
5536     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5537     gtl.codepage = CP_ACP;
5538     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5539     ok(ret == strlen(base_string), "ret %d\n",ret);
5540
5541     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5542
5543     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5544     gtl.codepage = CP_ACP;
5545     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5546     ok(ret == 1, "ret %d\n",ret);
5547
5548     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5549     gtl.codepage = CP_ACP;
5550     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5551     ok(ret == 1, "ret %d\n",ret);
5552
5553     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5554     ret = strcmp(buffer, test_string_after);
5555     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5556
5557     DestroyWindow(hwnd);
5558
5559     /* multi line */
5560     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5561                            0, 0, 200, 60, 0, 0, 0, 0);
5562     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5563
5564     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5565     gtl.codepage = CP_ACP;
5566     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5567     ok(ret == 0, "ret %d\n",ret);
5568
5569     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5570     gtl.codepage = CP_ACP;
5571     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5572     ok(ret == 0, "ret %d\n",ret);
5573
5574     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5575
5576     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5577     gtl.codepage = CP_ACP;
5578     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5579     ok(ret == strlen(base_string), "ret %d\n",ret);
5580
5581     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5582     gtl.codepage = CP_ACP;
5583     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5584     ok(ret == strlen(base_string), "ret %d\n",ret);
5585
5586     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5587
5588     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5589     gtl.codepage = CP_ACP;
5590     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5591     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5592
5593     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5594     gtl.codepage = CP_ACP;
5595     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5596     ok(ret == strlen(test_string_2), "ret %d\n",ret);
5597
5598     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5599
5600     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5601     gtl.codepage = CP_ACP;
5602     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5603     ok(ret == 10, "ret %d\n",ret);
5604
5605     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5606     gtl.codepage = CP_ACP;
5607     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5608     ok(ret == 6, "ret %d\n",ret);
5609
5610     /* Unicode/NUMCHARS/NUMBYTES */
5611     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5612
5613     gtl.flags = GTL_DEFAULT;
5614     gtl.codepage = 1200;
5615     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5616     ok(ret == lstrlen(test_string_2),
5617        "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5618
5619     gtl.flags = GTL_NUMCHARS;
5620     gtl.codepage = 1200;
5621     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5622     ok(ret == lstrlen(test_string_2),
5623        "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5624
5625     gtl.flags = GTL_NUMBYTES;
5626     gtl.codepage = 1200;
5627     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5628     ok(ret == lstrlen(test_string_2)*2,
5629        "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5630
5631     gtl.flags = GTL_PRECISE;
5632     gtl.codepage = 1200;
5633     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5634     ok(ret == lstrlen(test_string_2)*2,
5635        "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5636
5637     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5638     gtl.codepage = 1200;
5639     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5640     ok(ret == lstrlen(test_string_2),
5641        "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5642
5643     gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5644     gtl.codepage = 1200;
5645     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5646     ok(ret == E_INVALIDARG,
5647        "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5648
5649     DestroyWindow(hwnd);
5650 }
5651
5652
5653 /* globals that parent and child access when checking event masks & notifications */
5654 static HWND eventMaskEditHwnd = 0;
5655 static int queriedEventMask;
5656 static int watchForEventMask = 0;
5657
5658 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5659 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5660 {
5661     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5662     {
5663       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5664     }
5665     return DefWindowProcA(hwnd, message, wParam, lParam);
5666 }
5667
5668 /* test event masks in combination with WM_COMMAND */
5669 static void test_eventMask(void)
5670 {
5671     HWND parent;
5672     int ret, style;
5673     WNDCLASSA cls;
5674     const char text[] = "foo bar\n";
5675     int eventMask;
5676
5677     /* register class to capture WM_COMMAND */
5678     cls.style = 0;
5679     cls.lpfnWndProc = ParentMsgCheckProcA;
5680     cls.cbClsExtra = 0;
5681     cls.cbWndExtra = 0;
5682     cls.hInstance = GetModuleHandleA(0);
5683     cls.hIcon = 0;
5684     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5685     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5686     cls.lpszMenuName = NULL;
5687     cls.lpszClassName = "EventMaskParentClass";
5688     if(!RegisterClassA(&cls)) assert(0);
5689
5690     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5691                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5692     ok (parent != 0, "Failed to create parent window\n");
5693
5694     eventMaskEditHwnd = new_richedit(parent);
5695     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5696
5697     eventMask = ENM_CHANGE | ENM_UPDATE;
5698     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5699     ok(ret == ENM_NONE, "wrong event mask\n");
5700     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5701     ok(ret == eventMask, "failed to set event mask\n");
5702
5703     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5704     queriedEventMask = 0;  /* initialize to something other than we expect */
5705     watchForEventMask = EN_CHANGE;
5706     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5707     ok(ret == TRUE, "failed to set text\n");
5708     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5709        notification in response to WM_SETTEXT */
5710     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5711             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5712
5713     /* check to see if EN_CHANGE is sent when redraw is turned off */
5714     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5715     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5716     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5717     /* redraw is disabled by making the window invisible. */
5718     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5719     queriedEventMask = 0;  /* initialize to something other than we expect */
5720     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5721     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5722             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5723     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5724     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5725
5726     /* check to see if EN_UPDATE is sent when the editor isn't visible */
5727     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5728     style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5729     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5730     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5731     watchForEventMask = EN_UPDATE;
5732     queriedEventMask = 0;  /* initialize to something other than we expect */
5733     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5734     ok(queriedEventMask == 0,
5735             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5736     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5737     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5738     queriedEventMask = 0;  /* initialize to something other than we expect */
5739     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5740     ok(queriedEventMask == eventMask,
5741             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5742
5743
5744     DestroyWindow(parent);
5745 }
5746
5747 static int received_WM_NOTIFY = 0;
5748 static int modify_at_WM_NOTIFY = 0;
5749 static BOOL filter_on_WM_NOTIFY = FALSE;
5750 static HWND hwndRichedit_WM_NOTIFY;
5751
5752 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5753 {
5754     if(message == WM_NOTIFY)
5755     {
5756       received_WM_NOTIFY = 1;
5757       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5758       if (filter_on_WM_NOTIFY) return TRUE;
5759     }
5760     return DefWindowProcA(hwnd, message, wParam, lParam);
5761 }
5762
5763 static void test_WM_NOTIFY(void)
5764 {
5765     HWND parent;
5766     WNDCLASSA cls;
5767     CHARFORMAT2 cf2;
5768     int sel_start, sel_end;
5769
5770     /* register class to capture WM_NOTIFY */
5771     cls.style = 0;
5772     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5773     cls.cbClsExtra = 0;
5774     cls.cbWndExtra = 0;
5775     cls.hInstance = GetModuleHandleA(0);
5776     cls.hIcon = 0;
5777     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5778     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5779     cls.lpszMenuName = NULL;
5780     cls.lpszClassName = "WM_NOTIFY_ParentClass";
5781     if(!RegisterClassA(&cls)) assert(0);
5782
5783     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5784                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5785     ok (parent != 0, "Failed to create parent window\n");
5786
5787     hwndRichedit_WM_NOTIFY = new_richedit(parent);
5788     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5789
5790     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5791
5792     /* Notifications for selection change should only be sent when selection
5793        actually changes. EM_SETCHARFORMAT is one message that calls
5794        ME_CommitUndo, which should check whether message should be sent */
5795     received_WM_NOTIFY = 0;
5796     cf2.cbSize = sizeof(CHARFORMAT2);
5797     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5798     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5799     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5800     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5801     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5802
5803     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5804        already at 0. */
5805     received_WM_NOTIFY = 0;
5806     modify_at_WM_NOTIFY = 0;
5807     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5808     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5809     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5810
5811     received_WM_NOTIFY = 0;
5812     modify_at_WM_NOTIFY = 0;
5813     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5814     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5815
5816     received_WM_NOTIFY = 0;
5817     modify_at_WM_NOTIFY = 0;
5818     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5819     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5820     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5821
5822     /* Test for WM_NOTIFY messages with redraw disabled. */
5823     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5824     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5825     received_WM_NOTIFY = 0;
5826     SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5827     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5828     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5829
5830     /* Test filtering key events. */
5831     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5832     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5833     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5834     received_WM_NOTIFY = 0;
5835     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5836     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5837     ok(sel_start == 1 && sel_end == 1,
5838        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5839     filter_on_WM_NOTIFY = TRUE;
5840     received_WM_NOTIFY = 0;
5841     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5842     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5843     ok(sel_start == 1 && sel_end == 1,
5844        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5845
5846     /* test with owner set to NULL */
5847     SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5848     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5849     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5850     ok(sel_start == 1 && sel_end == 1,
5851        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5852
5853     DestroyWindow(hwndRichedit_WM_NOTIFY);
5854     DestroyWindow(parent);
5855 }
5856
5857 static void test_undo_coalescing(void)
5858 {
5859     HWND hwnd;
5860     int result;
5861     char buffer[64] = {0};
5862
5863     /* multi-line control inserts CR normally */
5864     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5865                            0, 0, 200, 60, 0, 0, 0, 0);
5866     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5867
5868     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5869     ok (result == FALSE, "Can undo after window creation.\n");
5870     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5871     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5872     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5873     ok (result == FALSE, "Can redo after window creation.\n");
5874     result = SendMessage(hwnd, EM_REDO, 0, 0);
5875     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5876
5877     /* Test the effect of arrows keys during typing on undo transactions*/
5878     simulate_typing_characters(hwnd, "one two three");
5879     SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5880     SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5881     simulate_typing_characters(hwnd, " four five six");
5882
5883     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5884     ok (result == FALSE, "Can redo before anything is undone.\n");
5885     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5886     ok (result == TRUE, "Cannot undo typed characters.\n");
5887     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5888     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5889     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5890     ok (result == TRUE, "Cannot redo after undo.\n");
5891     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5892     result = strcmp(buffer, "one two three");
5893     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5894
5895     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5896     ok (result == TRUE, "Cannot undo typed characters.\n");
5897     result = SendMessage(hwnd, WM_UNDO, 0, 0);
5898     ok (result == TRUE, "Failed to undo typed characters.\n");
5899     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5900     result = strcmp(buffer, "");
5901     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5902
5903     /* Test the effect of focus changes during typing on undo transactions*/
5904     simulate_typing_characters(hwnd, "one two three");
5905     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5906     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5907     SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5908     SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5909     simulate_typing_characters(hwnd, " four five six");
5910     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5911     ok (result == TRUE, "Failed to undo typed characters.\n");
5912     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5913     result = strcmp(buffer, "one two three");
5914     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5915
5916     /* Test the effect of the back key during typing on undo transactions */
5917     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5918     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5919     ok (result == TRUE, "Failed to clear the text.\n");
5920     simulate_typing_characters(hwnd, "one two threa");
5921     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5922     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5923     SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5924     SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5925     simulate_typing_characters(hwnd, "e four five six");
5926     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5927     ok (result == TRUE, "Failed to undo typed characters.\n");
5928     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5929     result = strcmp(buffer, "");
5930     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5931
5932     /* Test the effect of the delete key during typing on undo transactions */
5933     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5934     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5935     ok(result == TRUE, "Failed to set the text.\n");
5936     SendMessage(hwnd, EM_SETSEL, 1, 1);
5937     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5938     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5939     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5940     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5941     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5942     ok (result == TRUE, "Failed to undo typed characters.\n");
5943     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5944     result = strcmp(buffer, "acd");
5945     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5946     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5947     ok (result == TRUE, "Failed to undo typed characters.\n");
5948     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5949     result = strcmp(buffer, "abcd");
5950     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5951
5952     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5953     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5954     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5955     ok (result == TRUE, "Failed to clear the text.\n");
5956     simulate_typing_characters(hwnd, "one two three");
5957     result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5958     ok (result == 0, "expected %d but got %d\n", 0, result);
5959     simulate_typing_characters(hwnd, " four five six");
5960     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5961     ok (result == TRUE, "Failed to undo typed characters.\n");
5962     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5963     result = strcmp(buffer, "one two three");
5964     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5965     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5966     ok (result == TRUE, "Failed to undo typed characters.\n");
5967     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5968     result = strcmp(buffer, "");
5969     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5970
5971     DestroyWindow(hwnd);
5972 }
5973
5974 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5975 {
5976     int length;
5977
5978     /* MSDN lied, length is actually the number of bytes. */
5979     length = bytes / sizeof(WCHAR);
5980     switch(code)
5981     {
5982         case WB_ISDELIMITER:
5983             return text[pos] == 'X';
5984         case WB_LEFT:
5985         case WB_MOVEWORDLEFT:
5986             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5987                 return pos-1;
5988             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5989         case WB_LEFTBREAK:
5990             pos--;
5991             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5992                 pos--;
5993             return pos;
5994         case WB_RIGHT:
5995         case WB_MOVEWORDRIGHT:
5996             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5997                 return pos+1;
5998             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5999         case WB_RIGHTBREAK:
6000             pos++;
6001             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6002                 pos++;
6003             return pos;
6004         default:
6005             ok(FALSE, "Unexpected code %d\n", code);
6006             break;
6007     }
6008     return 0;
6009 }
6010
6011 static void test_word_movement(void)
6012 {
6013     HWND hwnd;
6014     int result;
6015     int sel_start, sel_end;
6016     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6017
6018     /* multi-line control inserts CR normally */
6019     hwnd = new_richedit(NULL);
6020
6021     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
6022     ok (result == TRUE, "Failed to clear the text.\n");
6023     SendMessage(hwnd, EM_SETSEL, 0, 0);
6024     /* |one two three */
6025
6026     send_ctrl_key(hwnd, VK_RIGHT);
6027     /* one |two  three */
6028     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6029     ok(sel_start == sel_end, "Selection should be empty\n");
6030     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6031
6032     send_ctrl_key(hwnd, VK_RIGHT);
6033     /* one two  |three */
6034     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6035     ok(sel_start == sel_end, "Selection should be empty\n");
6036     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6037
6038     send_ctrl_key(hwnd, VK_LEFT);
6039     /* one |two  three */
6040     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6041     ok(sel_start == sel_end, "Selection should be empty\n");
6042     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6043
6044     send_ctrl_key(hwnd, VK_LEFT);
6045     /* |one two  three */
6046     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6047     ok(sel_start == sel_end, "Selection should be empty\n");
6048     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6049
6050     SendMessage(hwnd, EM_SETSEL, 8, 8);
6051     /* one two | three */
6052     send_ctrl_key(hwnd, VK_RIGHT);
6053     /* one two  |three */
6054     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6055     ok(sel_start == sel_end, "Selection should be empty\n");
6056     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6057
6058     SendMessage(hwnd, EM_SETSEL, 11, 11);
6059     /* one two  th|ree */
6060     send_ctrl_key(hwnd, VK_LEFT);
6061     /* one two  |three */
6062     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6063     ok(sel_start == sel_end, "Selection should be empty\n");
6064     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6065
6066     /* Test with a custom word break procedure that uses X as the delimiter. */
6067     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6068     ok (result == TRUE, "Failed to clear the text.\n");
6069     SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6070     /* |one twoXthree */
6071     send_ctrl_key(hwnd, VK_RIGHT);
6072     /* one twoX|three */
6073     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6074     ok(sel_start == sel_end, "Selection should be empty\n");
6075     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6076
6077     DestroyWindow(hwnd);
6078
6079     /* Make sure the behaviour is the same with a unicode richedit window,
6080      * and using unicode functions. */
6081
6082     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6083                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6084                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6085
6086     /* Test with a custom word break procedure that uses X as the delimiter. */
6087     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6088     ok (result == TRUE, "Failed to clear the text.\n");
6089     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6090     /* |one twoXthree */
6091     send_ctrl_key(hwnd, VK_RIGHT);
6092     /* one twoX|three */
6093     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6094     ok(sel_start == sel_end, "Selection should be empty\n");
6095     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6096
6097     DestroyWindow(hwnd);
6098 }
6099
6100 static void test_EM_CHARFROMPOS(void)
6101 {
6102     HWND hwnd;
6103     int result;
6104     RECT rcClient;
6105     POINTL point;
6106     point.x = 0;
6107     point.y = 40;
6108
6109     /* multi-line control inserts CR normally */
6110     hwnd = new_richedit(NULL);
6111     result = SendMessageA(hwnd, WM_SETTEXT, 0,
6112                           (LPARAM)"one two three four five six seven\reight");
6113     ok(result == 1, "Expected 1, got %d\n", result);
6114     GetClientRect(hwnd, &rcClient);
6115
6116     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6117     ok(result == 34, "expected character index of 34 but got %d\n", result);
6118
6119     /* Test with points outside the bounds of the richedit control. */
6120     point.x = -1;
6121     point.y = 40;
6122     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6123     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6124
6125     point.x = 1000;
6126     point.y = 0;
6127     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6128     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6129
6130     point.x = 1000;
6131     point.y = 36;
6132     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6133     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6134
6135     point.x = 1000;
6136     point.y = -1;
6137     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6138     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6139
6140     point.x = 1000;
6141     point.y = rcClient.bottom + 1;
6142     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6143     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6144
6145     point.x = 1000;
6146     point.y = rcClient.bottom;
6147     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6148     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6149
6150     DestroyWindow(hwnd);
6151 }
6152
6153 static void test_word_wrap(void)
6154 {
6155     HWND hwnd;
6156     POINTL point = {0, 60}; /* This point must be below the first line */
6157     const char *text = "Must be long enough to test line wrapping";
6158     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6159     int res, pos, lines;
6160
6161     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6162      * when specified on window creation and set later. */
6163     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6164                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6165     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6166     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6167     ok(res, "WM_SETTEXT failed.\n");
6168     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6169     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6170     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6171     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6172
6173     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6174     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6175     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6176     DestroyWindow(hwnd);
6177
6178     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6179                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6180     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6181
6182     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6183     ok(res, "WM_SETTEXT failed.\n");
6184     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6185     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6186     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6187     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6188
6189     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6190     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6191     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6192     DestroyWindow(hwnd);
6193
6194     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6195                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6196     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6197     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6198     ok(res, "WM_SETTEXT failed.\n");
6199     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6200     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6201
6202     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6203     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6204     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6205     DestroyWindow(hwnd);
6206
6207     hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6208                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6209                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6210     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6211     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6212     ok(res, "WM_SETTEXT failed.\n");
6213     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6214     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6215
6216     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6217     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6218     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6219
6220     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6221     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6222     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6223     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6224     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6225
6226     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6227     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6228     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6229     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6230     DestroyWindow(hwnd);
6231
6232     /* Test to see if wrapping happens with redraw disabled. */
6233     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6234                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6235     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6236     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6237     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6238     ok(res, "EM_REPLACESEL failed.\n");
6239     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6240     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6241     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6242     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6243     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6244
6245     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6246     DestroyWindow(hwnd);
6247 }
6248
6249 static void test_autoscroll(void)
6250 {
6251     HWND hwnd = new_richedit(NULL);
6252     int lines, ret, redraw;
6253     POINT pt;
6254
6255     for (redraw = 0; redraw <= 1; redraw++) {
6256         trace("testing with WM_SETREDRAW=%d\n", redraw);
6257         SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6258         SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6259         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6260         ok(lines == 8, "%d lines instead of 8\n", lines);
6261         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6262         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6263         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6264         ret = GetWindowLong(hwnd, GWL_STYLE);
6265         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6266
6267         SendMessage(hwnd, WM_SETTEXT, 0, 0);
6268         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6269         ok(lines == 1, "%d lines instead of 1\n", lines);
6270         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6271         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6272         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6273         ret = GetWindowLong(hwnd, GWL_STYLE);
6274         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6275     }
6276
6277     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6278     DestroyWindow(hwnd);
6279
6280     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6281      * auto vertical/horizontal scrolling options. */
6282     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6283                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6284                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6285     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6286     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6287     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6288     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6289     ret = GetWindowLong(hwnd, GWL_STYLE);
6290     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6291     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6292     DestroyWindow(hwnd);
6293
6294     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6295                           WS_POPUP|ES_MULTILINE,
6296                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6297     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6298     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6299     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6300     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6301     ret = GetWindowLong(hwnd, GWL_STYLE);
6302     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6303     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6304     DestroyWindow(hwnd);
6305 }
6306
6307
6308 static void test_format_rect(void)
6309 {
6310     HWND hwnd;
6311     RECT rc, expected, clientRect;
6312     int n;
6313     DWORD options;
6314
6315     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6316                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6317                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6318     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6319
6320     GetClientRect(hwnd, &clientRect);
6321
6322     expected = clientRect;
6323     expected.left += 1;
6324     expected.right -= 1;
6325     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6326     ok(rc.top == expected.top && rc.left == expected.left &&
6327        rc.bottom == expected.bottom && rc.right == expected.right,
6328        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6329        rc.top, rc.left, rc.bottom, rc.right,
6330        expected.top, expected.left, expected.bottom, expected.right);
6331
6332     for (n = -3; n <= 3; n++)
6333     {
6334       rc = clientRect;
6335       rc.top += n;
6336       rc.left += n;
6337       rc.bottom -= n;
6338       rc.right -= n;
6339       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6340
6341       expected = rc;
6342       expected.top = max(0, rc.top);
6343       expected.left = max(0, rc.left);
6344       expected.bottom = min(clientRect.bottom, rc.bottom);
6345       expected.right = min(clientRect.right, rc.right);
6346       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6347       ok(rc.top == expected.top && rc.left == expected.left &&
6348          rc.bottom == expected.bottom && rc.right == expected.right,
6349          "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6350          n, rc.top, rc.left, rc.bottom, rc.right,
6351          expected.top, expected.left, expected.bottom, expected.right);
6352     }
6353
6354     rc = clientRect;
6355     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6356     expected = clientRect;
6357     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6358     ok(rc.top == expected.top && rc.left == expected.left &&
6359        rc.bottom == expected.bottom && rc.right == expected.right,
6360        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6361        rc.top, rc.left, rc.bottom, rc.right,
6362        expected.top, expected.left, expected.bottom, expected.right);
6363
6364     /* Adding the selectionbar adds the selectionbar width to the left side. */
6365     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6366     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6367     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6368     expected.left += 8; /* selection bar width */
6369     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6370     ok(rc.top == expected.top && rc.left == expected.left &&
6371        rc.bottom == expected.bottom && rc.right == expected.right,
6372        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6373        rc.top, rc.left, rc.bottom, rc.right,
6374        expected.top, expected.left, expected.bottom, expected.right);
6375
6376     rc = clientRect;
6377     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6378     expected = clientRect;
6379     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6380     ok(rc.top == expected.top && rc.left == expected.left &&
6381        rc.bottom == expected.bottom && rc.right == expected.right,
6382        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6383        rc.top, rc.left, rc.bottom, rc.right,
6384        expected.top, expected.left, expected.bottom, expected.right);
6385
6386     /* Removing the selectionbar subtracts the selectionbar width from the left side,
6387      * even if the left side is already 0. */
6388     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6389     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6390     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6391     expected.left -= 8; /* selection bar width */
6392     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6393     ok(rc.top == expected.top && rc.left == expected.left &&
6394        rc.bottom == expected.bottom && rc.right == expected.right,
6395        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6396        rc.top, rc.left, rc.bottom, rc.right,
6397        expected.top, expected.left, expected.bottom, expected.right);
6398
6399     /* Set the absolute value of the formatting rectangle. */
6400     rc = clientRect;
6401     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6402     expected = clientRect;
6403     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6404     ok(rc.top == expected.top && rc.left == expected.left &&
6405        rc.bottom == expected.bottom && rc.right == expected.right,
6406        "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6407        n, rc.top, rc.left, rc.bottom, rc.right,
6408        expected.top, expected.left, expected.bottom, expected.right);
6409
6410     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6411      * LPARAM as being a relative offset when the WPARAM value is 1, but these
6412      * tests show that this isn't true. */
6413     rc.top = 15;
6414     rc.left = 15;
6415     rc.bottom = clientRect.bottom - 15;
6416     rc.right = clientRect.right - 15;
6417     expected = rc;
6418     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6419     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6420     ok(rc.top == expected.top && rc.left == expected.left &&
6421        rc.bottom == expected.bottom && rc.right == expected.right,
6422        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6423        rc.top, rc.left, rc.bottom, rc.right,
6424        expected.top, expected.left, expected.bottom, expected.right);
6425
6426     /* For some reason it does not limit the values to the client rect with
6427      * a WPARAM value of 1. */
6428     rc.top = -15;
6429     rc.left = -15;
6430     rc.bottom = clientRect.bottom + 15;
6431     rc.right = clientRect.right + 15;
6432     expected = rc;
6433     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6434     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6435     ok(rc.top == expected.top && rc.left == expected.left &&
6436        rc.bottom == expected.bottom && rc.right == expected.right,
6437        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6438        rc.top, rc.left, rc.bottom, rc.right,
6439        expected.top, expected.left, expected.bottom, expected.right);
6440
6441     /* Reset to default rect and check how the format rect adjusts to window
6442      * resize and how it copes with very small windows */
6443     SendMessageA(hwnd, EM_SETRECT, 0, 0);
6444
6445     MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6446     GetClientRect(hwnd, &clientRect);
6447
6448     expected = clientRect;
6449     expected.left += 1;
6450     expected.right -= 1;
6451     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6452     ok(rc.top == expected.top && rc.left == expected.left &&
6453        rc.bottom == expected.bottom && rc.right == expected.right,
6454        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6455        rc.top, rc.left, rc.bottom, rc.right,
6456        expected.top, expected.left, expected.bottom, expected.right);
6457
6458     MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6459     GetClientRect(hwnd, &clientRect);
6460
6461     expected = clientRect;
6462     expected.left += 1;
6463     expected.right -= 1;
6464     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6465     ok(rc.top == expected.top && rc.left == expected.left &&
6466        rc.bottom == expected.bottom && rc.right == expected.right,
6467        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6468        rc.top, rc.left, rc.bottom, rc.right,
6469        expected.top, expected.left, expected.bottom, expected.right);
6470
6471     MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6472     GetClientRect(hwnd, &clientRect);
6473
6474     expected = clientRect;
6475     expected.left += 1;
6476     expected.right -= 1;
6477     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6478     ok(rc.top == expected.top && rc.left == expected.left &&
6479        rc.bottom == expected.bottom && rc.right == expected.right,
6480        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6481        rc.top, rc.left, rc.bottom, rc.right,
6482        expected.top, expected.left, expected.bottom, expected.right);
6483
6484     DestroyWindow(hwnd);
6485
6486     /* The extended window style affects the formatting rectangle. */
6487     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6488                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6489                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6490     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6491
6492     GetClientRect(hwnd, &clientRect);
6493
6494     expected = clientRect;
6495     expected.left += 1;
6496     expected.top += 1;
6497     expected.right -= 1;
6498     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6499     ok(rc.top == expected.top && rc.left == expected.left &&
6500        rc.bottom == expected.bottom && rc.right == expected.right,
6501        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6502        rc.top, rc.left, rc.bottom, rc.right,
6503        expected.top, expected.left, expected.bottom, expected.right);
6504
6505     rc = clientRect;
6506     rc.top += 5;
6507     rc.left += 5;
6508     rc.bottom -= 5;
6509     rc.right -= 5;
6510     expected = rc;
6511     expected.top -= 1;
6512     expected.left -= 1;
6513     expected.right += 1;
6514     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6515     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6516     ok(rc.top == expected.top && rc.left == expected.left &&
6517        rc.bottom == expected.bottom && rc.right == expected.right,
6518        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6519        rc.top, rc.left, rc.bottom, rc.right,
6520        expected.top, expected.left, expected.bottom, expected.right);
6521
6522     DestroyWindow(hwnd);
6523 }
6524
6525 static void test_WM_GETDLGCODE(void)
6526 {
6527     HWND hwnd;
6528     UINT res, expected;
6529     MSG msg;
6530
6531     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6532
6533     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6534                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6535                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6536     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6537     msg.hwnd = hwnd;
6538     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6539     expected = expected | DLGC_WANTMESSAGE;
6540     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6541        res, expected);
6542     DestroyWindow(hwnd);
6543
6544     msg.message = WM_KEYDOWN;
6545     msg.wParam = VK_RETURN;
6546     msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6547     msg.pt.x = 0;
6548     msg.pt.y = 0;
6549     msg.time = GetTickCount();
6550
6551     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6552                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6553                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6554     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6555     msg.hwnd = hwnd;
6556     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6557     expected = expected | DLGC_WANTMESSAGE;
6558     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6559        res, expected);
6560     DestroyWindow(hwnd);
6561
6562     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6563                           ES_MULTILINE|WS_POPUP,
6564                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6565     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6566     msg.hwnd = hwnd;
6567     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6568     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6569     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6570        res, expected);
6571     DestroyWindow(hwnd);
6572
6573     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6574                           ES_WANTRETURN|WS_POPUP,
6575                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6576     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6577     msg.hwnd = hwnd;
6578     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6579     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6580     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6581        res, expected);
6582     DestroyWindow(hwnd);
6583
6584     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6585                           WS_POPUP,
6586                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6587     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6588     msg.hwnd = hwnd;
6589     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6590     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6591     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6592        res, expected);
6593     DestroyWindow(hwnd);
6594
6595     msg.wParam = VK_TAB;
6596     msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6597
6598     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6599                           ES_MULTILINE|WS_POPUP,
6600                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6601     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6602     msg.hwnd = hwnd;
6603     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6604     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6605     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6606        res, expected);
6607     DestroyWindow(hwnd);
6608
6609     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6610                           WS_POPUP,
6611                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6612     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6613     msg.hwnd = hwnd;
6614     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6615     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6616     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6617        res, expected);
6618     DestroyWindow(hwnd);
6619
6620     hold_key(VK_CONTROL);
6621
6622     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6623                           ES_MULTILINE|WS_POPUP,
6624                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6625     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6626     msg.hwnd = hwnd;
6627     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6628     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6629     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6630        res, expected);
6631     DestroyWindow(hwnd);
6632
6633     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6634                           WS_POPUP,
6635                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6636     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6637     msg.hwnd = hwnd;
6638     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6639     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6640     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6641        res, expected);
6642     DestroyWindow(hwnd);
6643
6644     release_key(VK_CONTROL);
6645
6646     msg.wParam = 'a';
6647     msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6648
6649     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6650                           ES_MULTILINE|WS_POPUP,
6651                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6652     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6653     msg.hwnd = hwnd;
6654     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6655     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6656     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6657        res, expected);
6658     DestroyWindow(hwnd);
6659
6660     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6661                           WS_POPUP,
6662                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6663     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6664     msg.hwnd = hwnd;
6665     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6666     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6667     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6668        res, expected);
6669     DestroyWindow(hwnd);
6670
6671     msg.message = WM_CHAR;
6672
6673     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6674                           ES_MULTILINE|WS_POPUP,
6675                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6676     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6677     msg.hwnd = hwnd;
6678     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6679     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6680     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6681        res, expected);
6682     DestroyWindow(hwnd);
6683
6684     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6685                           WS_POPUP,
6686                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6687     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6688     msg.hwnd = hwnd;
6689     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6690     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6691     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6692        res, expected);
6693     DestroyWindow(hwnd);
6694 }
6695
6696 static void test_zoom(void)
6697 {
6698     HWND hwnd;
6699     UINT ret;
6700     RECT rc;
6701     POINT pt;
6702     int numerator, denominator;
6703
6704     hwnd = new_richedit(NULL);
6705     GetClientRect(hwnd, &rc);
6706     pt.x = (rc.right - rc.left) / 2;
6707     pt.y = (rc.bottom - rc.top) / 2;
6708     ClientToScreen(hwnd, &pt);
6709
6710     /* Test initial zoom value */
6711     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6712     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6713     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6714     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6715
6716     /* test scroll wheel */
6717     hold_key(VK_CONTROL);
6718     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6719                       MAKELPARAM(pt.x, pt.y));
6720     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6721     release_key(VK_CONTROL);
6722
6723     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6724     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6725     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6726     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6727
6728     /* Test how much the mouse wheel can zoom in and out. */
6729     ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6730     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6731
6732     hold_key(VK_CONTROL);
6733     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6734                       MAKELPARAM(pt.x, pt.y));
6735     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6736     release_key(VK_CONTROL);
6737
6738     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6739     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6740     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6741     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6742
6743     ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6744     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6745
6746     hold_key(VK_CONTROL);
6747     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6748                       MAKELPARAM(pt.x, pt.y));
6749     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6750     release_key(VK_CONTROL);
6751
6752     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6753     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6754     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6755     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6756
6757     ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6758     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6759
6760     hold_key(VK_CONTROL);
6761     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6762                       MAKELPARAM(pt.x, pt.y));
6763     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6764     release_key(VK_CONTROL);
6765
6766     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6767     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6768     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6769     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6770
6771     ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6772     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6773
6774     hold_key(VK_CONTROL);
6775     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6776                       MAKELPARAM(pt.x, pt.y));
6777     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6778     release_key(VK_CONTROL);
6779
6780     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6781     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6782     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6783     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6784
6785     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6786     ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6787     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6788
6789     hold_key(VK_CONTROL);
6790     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6791                       MAKELPARAM(pt.x, pt.y));
6792     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6793     release_key(VK_CONTROL);
6794
6795     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6796     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6797     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6798     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6799
6800     /* Test bounds checking on EM_SETZOOM */
6801     ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6802     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6803
6804     ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6805     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6806
6807     ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6808     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6809
6810     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6811     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6812     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6813     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6814
6815     ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6816     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6817
6818     /* See if negative numbers are accepted. */
6819     ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6820     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6821
6822     /* See if negative numbers are accepted. */
6823     ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6824     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6825
6826     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6827     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6828     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6829     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6830
6831     /* Reset the zoom value */
6832     ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6833     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6834
6835     DestroyWindow(hwnd);
6836 }
6837
6838 struct dialog_mode_messages
6839 {
6840     int wm_getdefid, wm_close, wm_nextdlgctl;
6841 };
6842
6843 static struct dialog_mode_messages dm_messages;
6844
6845 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6846     ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6847     "got %d\n", wmclose, dm_messages.wm_close); \
6848     ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6849     "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6850     ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6851     "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6852
6853 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6854 {
6855     switch (iMsg)
6856     {
6857         case DM_GETDEFID:
6858             dm_messages.wm_getdefid++;
6859             return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6860         case WM_NEXTDLGCTL:
6861             dm_messages.wm_nextdlgctl++;
6862             break;
6863         case WM_CLOSE:
6864             dm_messages.wm_close++;
6865             break;
6866     }
6867
6868     return DefWindowProc(hwnd, iMsg, wParam, lParam);
6869 }
6870
6871 static void test_dialogmode(void)
6872 {
6873     HWND hwRichEdit, hwParent, hwButton;
6874     MSG msg= {0};
6875     int lcount, r;
6876     WNDCLASSA cls;
6877
6878     cls.style = 0;
6879     cls.lpfnWndProc = dialog_mode_wnd_proc;
6880     cls.cbClsExtra = 0;
6881     cls.cbWndExtra = 0;
6882     cls.hInstance = GetModuleHandleA(0);
6883     cls.hIcon = 0;
6884     cls.hCursor = LoadCursorA(0, IDC_ARROW);
6885     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6886     cls.lpszMenuName = NULL;
6887     cls.lpszClassName = "DialogModeParentClass";
6888     if(!RegisterClassA(&cls)) assert(0);
6889
6890     hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6891       CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6892
6893     /* Test richedit(ES_MULTILINE) */
6894
6895     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6896
6897     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6898     ok(0 == r, "expected 0, got %d\n", r);
6899     lcount = SendMessage(hwRichEdit,  EM_GETLINECOUNT, 0, 0);
6900     ok(2 == lcount, "expected 2, got %d\n", lcount);
6901
6902     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6903     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6904
6905     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6906     ok(0 == r, "expected 0, got %d\n", r);
6907     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6908     ok(3 == lcount, "expected 3, got %d\n", lcount);
6909
6910     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6911     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6912     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6913     ok(0 == r, "expected 0, got %d\n", r);
6914     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6915     ok(3 == lcount, "expected 3, got %d\n", lcount);
6916
6917     DestroyWindow(hwRichEdit);
6918
6919     /* Test standalone richedit(ES_MULTILINE) */
6920
6921     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6922
6923     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6924     ok(0 == r, "expected 0, got %d\n", r);
6925     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6926     ok(2 == lcount, "expected 2, got %d\n", lcount);
6927
6928     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6929     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6930
6931     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6932     ok(0 == r, "expected 0, got %d\n", r);
6933     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6934     ok(2 == lcount, "expected 2, got %d\n", lcount);
6935
6936     DestroyWindow(hwRichEdit);
6937
6938     /* Check  a destination for messages */
6939
6940     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6941
6942     SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6943     SetParent( hwRichEdit, NULL);
6944
6945     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6946     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6947
6948     memset(&dm_messages, 0, sizeof(dm_messages));
6949     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6950     ok(0 == r, "expected 0, got %d\n", r);
6951     test_dm_messages(0, 1, 0);
6952
6953     memset(&dm_messages, 0, sizeof(dm_messages));
6954     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6955     ok(0 == r, "expected 0, got %d\n", r);
6956     test_dm_messages(0, 0, 1);
6957
6958     DestroyWindow(hwRichEdit);
6959
6960     /* Check messages from richedit(ES_MULTILINE) */
6961
6962     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6963
6964     memset(&dm_messages, 0, sizeof(dm_messages));
6965     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6966     ok(0 == r, "expected 0, got %d\n", r);
6967     test_dm_messages(0, 0, 0);
6968
6969     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6970     ok(2 == lcount, "expected 2, got %d\n", lcount);
6971
6972     memset(&dm_messages, 0, sizeof(dm_messages));
6973     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6974     ok(0 == r, "expected 0, got %d\n", r);
6975     test_dm_messages(0, 0, 0);
6976
6977     memset(&dm_messages, 0, sizeof(dm_messages));
6978     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6979     ok(0 == r, "expected 0, got %d\n", r);
6980     test_dm_messages(0, 0, 0);
6981
6982     memset(&dm_messages, 0, sizeof(dm_messages));
6983     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6984     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6985     test_dm_messages(0, 0, 0);
6986
6987     memset(&dm_messages, 0, sizeof(dm_messages));
6988     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6989     ok(0 == r, "expected 0, got %d\n", r);
6990     test_dm_messages(0, 1, 0);
6991
6992     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6993     ok(2 == lcount, "expected 2, got %d\n", lcount);
6994
6995     memset(&dm_messages, 0, sizeof(dm_messages));
6996     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6997     ok(0 == r, "expected 0, got %d\n", r);
6998     test_dm_messages(0, 0, 0);
6999
7000     memset(&dm_messages, 0, sizeof(dm_messages));
7001     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7002     ok(0 == r, "expected 0, got %d\n", r);
7003     test_dm_messages(0, 0, 1);
7004
7005     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7006         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7007     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7008
7009     memset(&dm_messages, 0, sizeof(dm_messages));
7010     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7011     ok(0 == r, "expected 0, got %d\n", r);
7012     test_dm_messages(0, 1, 1);
7013
7014     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7015     ok(2 == lcount, "expected 2, got %d\n", lcount);
7016
7017     DestroyWindow(hwButton);
7018     DestroyWindow(hwRichEdit);
7019
7020     /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7021
7022     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
7023
7024     memset(&dm_messages, 0, sizeof(dm_messages));
7025     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7026     ok(0 == r, "expected 0, got %d\n", r);
7027     test_dm_messages(0, 0, 0);
7028
7029     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7030     ok(2 == lcount, "expected 2, got %d\n", lcount);
7031
7032     memset(&dm_messages, 0, sizeof(dm_messages));
7033     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7034     ok(0 == r, "expected 0, got %d\n", r);
7035     test_dm_messages(0, 0, 0);
7036
7037     memset(&dm_messages, 0, sizeof(dm_messages));
7038     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7039     ok(0 == r, "expected 0, got %d\n", r);
7040     test_dm_messages(0, 0, 0);
7041
7042     memset(&dm_messages, 0, sizeof(dm_messages));
7043     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7044     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7045     test_dm_messages(0, 0, 0);
7046
7047     memset(&dm_messages, 0, sizeof(dm_messages));
7048     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7049     ok(0 == r, "expected 0, got %d\n", r);
7050     test_dm_messages(0, 0, 0);
7051
7052     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7053     ok(3 == lcount, "expected 3, got %d\n", lcount);
7054
7055     memset(&dm_messages, 0, sizeof(dm_messages));
7056     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7057     ok(0 == r, "expected 0, got %d\n", r);
7058     test_dm_messages(0, 0, 0);
7059
7060     memset(&dm_messages, 0, sizeof(dm_messages));
7061     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7062     ok(0 == r, "expected 0, got %d\n", r);
7063     test_dm_messages(0, 0, 1);
7064
7065     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7066         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7067     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7068
7069     memset(&dm_messages, 0, sizeof(dm_messages));
7070     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7071     ok(0 == r, "expected 0, got %d\n", r);
7072     test_dm_messages(0, 0, 0);
7073
7074     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7075     ok(4 == lcount, "expected 4, got %d\n", lcount);
7076
7077     DestroyWindow(hwButton);
7078     DestroyWindow(hwRichEdit);
7079
7080     /* Check messages from richedit(0) */
7081
7082     hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7083
7084     memset(&dm_messages, 0, sizeof(dm_messages));
7085     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7086     ok(0 == r, "expected 0, got %d\n", r);
7087     test_dm_messages(0, 0, 0);
7088
7089     memset(&dm_messages, 0, sizeof(dm_messages));
7090     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7091     ok(0 == r, "expected 0, got %d\n", r);
7092     test_dm_messages(0, 0, 0);
7093
7094     memset(&dm_messages, 0, sizeof(dm_messages));
7095     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7096     ok(0 == r, "expected 0, got %d\n", r);
7097     test_dm_messages(0, 0, 0);
7098
7099     memset(&dm_messages, 0, sizeof(dm_messages));
7100     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7101     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7102     test_dm_messages(0, 0, 0);
7103
7104     memset(&dm_messages, 0, sizeof(dm_messages));
7105     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7106     ok(0 == r, "expected 0, got %d\n", r);
7107     test_dm_messages(0, 1, 0);
7108
7109     memset(&dm_messages, 0, sizeof(dm_messages));
7110     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7111     ok(0 == r, "expected 0, got %d\n", r);
7112     test_dm_messages(0, 0, 0);
7113
7114     memset(&dm_messages, 0, sizeof(dm_messages));
7115     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7116     ok(0 == r, "expected 0, got %d\n", r);
7117     test_dm_messages(0, 0, 1);
7118
7119     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7120         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7121     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7122
7123     memset(&dm_messages, 0, sizeof(dm_messages));
7124     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7125     ok(0 == r, "expected 0, got %d\n", r);
7126     test_dm_messages(0, 1, 1);
7127
7128     DestroyWindow(hwRichEdit);
7129
7130     /* Check messages from richedit(ES_WANTRETURN) */
7131
7132     hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7133
7134     memset(&dm_messages, 0, sizeof(dm_messages));
7135     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7136     ok(0 == r, "expected 0, got %d\n", r);
7137     test_dm_messages(0, 0, 0);
7138
7139     memset(&dm_messages, 0, sizeof(dm_messages));
7140     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7141     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7142     test_dm_messages(0, 0, 0);
7143
7144     memset(&dm_messages, 0, sizeof(dm_messages));
7145     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7146     ok(0 == r, "expected 0, got %d\n", r);
7147     test_dm_messages(0, 0, 0);
7148
7149     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7150         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7151     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7152
7153     memset(&dm_messages, 0, sizeof(dm_messages));
7154     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7155     ok(0 == r, "expected 0, got %d\n", r);
7156     test_dm_messages(0, 0, 0);
7157
7158     DestroyWindow(hwRichEdit);
7159     DestroyWindow(hwParent);
7160 }
7161
7162 static void test_EM_FINDWORDBREAK_W(void)
7163 {
7164     static const struct {
7165         WCHAR c;
7166         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
7167     } delimiter_tests[] = {
7168         {0x0a,   FALSE},         /* newline */
7169         {0x0b,   FALSE},         /* vertical tab */
7170         {0x0c,   FALSE},         /* form feed */
7171         {0x0d,   FALSE},         /* carriage return */
7172         {0x20,   TRUE},          /* space */
7173         {0x61,   FALSE},         /* capital letter a */
7174         {0xa0,   FALSE},         /* no-break space */
7175         {0x2000, FALSE},         /* en quad */
7176         {0x3000, FALSE},         /* Ideographic space */
7177         {0x1100, FALSE},         /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7178         {0x11ff, FALSE},         /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7179         {0x115f, FALSE},         /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7180         {0xac00, FALSE},         /* Hangul character GA*/
7181         {0xd7af, FALSE},         /* End of Hangul character chart */
7182         {0xf020, TRUE},          /* MS private for CP_SYMBOL round trip?, see kb897872 */
7183         {0xff20, FALSE},         /* fullwidth commercial @ */
7184         {WCH_EMBEDDING, FALSE},  /* object replacement character*/
7185     };
7186     int i;
7187     HWND hwndRichEdit = new_richeditW(NULL);
7188     ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7189     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7190     {
7191         WCHAR wbuf[2];
7192         int result;
7193
7194         wbuf[0] = delimiter_tests[i].c;
7195         wbuf[1] = 0;
7196         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7197         result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7198         if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7199             todo_wine
7200                 ok(result == delimiter_tests[i].isdelimiter,
7201                    "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7202                    delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7203         else
7204             ok(result == delimiter_tests[i].isdelimiter,
7205                "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7206                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7207     }
7208     DestroyWindow(hwndRichEdit);
7209 }
7210
7211 static void test_EM_FINDWORDBREAK_A(void)
7212 {
7213     static const struct {
7214         WCHAR c;
7215         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
7216     } delimiter_tests[] = {
7217         {0x0a,   FALSE},         /* newline */
7218         {0x0b,   FALSE},         /* vertical tab */
7219         {0x0c,   FALSE},         /* form feed */
7220         {0x0d,   FALSE},         /* carriage return */
7221         {0x20,   TRUE},          /* space */
7222         {0x61,   FALSE},         /* capital letter a */
7223     };
7224     int i;
7225     HWND hwndRichEdit = new_richedit(NULL);
7226
7227     ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7228     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7229     {
7230         int result;
7231         char buf[2];
7232         buf[0] = delimiter_tests[i].c;
7233         buf[1] = 0;
7234         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7235         result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7236         if (buf[0] == 0x20)
7237             todo_wine
7238                 ok(result == delimiter_tests[i].isdelimiter,
7239                    "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7240                    delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7241         else
7242             ok(result == delimiter_tests[i].isdelimiter,
7243                "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7244                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7245     }
7246     DestroyWindow(hwndRichEdit);
7247 }
7248
7249 /*
7250  * This test attempts to show the effect of enter on a richedit
7251  * control v1.0 inserts CRLF whereas for higher versions it only
7252  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7253  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7254  * does for higher. The same test is cloned in riched32 and riched20.
7255  */
7256 static void test_enter(void)
7257 {
7258     static const struct {
7259       const char *initialtext;
7260       const int   cursor;
7261       const char *expectedwmtext;
7262       const char *expectedemtext;
7263       const char *expectedemtextcrlf;
7264     } testenteritems[] = {
7265       { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7266       { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7267       { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7268       { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7269       { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7270     };
7271
7272   char expectedbuf[1024];
7273   char resultbuf[1024];
7274   HWND hwndRichEdit = new_richedit(NULL);
7275   UINT i,j;
7276
7277   for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7278
7279     char buf[1024] = {0};
7280     LRESULT result;
7281     GETTEXTEX getText;
7282     const char *expected;
7283
7284     /* Set the text to the initial text */
7285     result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
7286     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7287
7288     /* Send Enter */
7289     SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7290     simulate_typing_characters(hwndRichEdit, "\r");
7291
7292     /* 1. Retrieve with WM_GETTEXT */
7293     buf[0] = 0x00;
7294     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
7295     expected = testenteritems[i].expectedwmtext;
7296
7297     resultbuf[0]=0x00;
7298     for (j = 0; j < (UINT)result; j++)
7299       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7300     expectedbuf[0] = '\0';
7301     for (j = 0; j < strlen(expected); j++)
7302       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7303
7304     result = strcmp(expected, buf);
7305     ok (result == 0,
7306         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7307         i, resultbuf, expectedbuf);
7308
7309     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7310     getText.cb = sizeof(buf);
7311     getText.flags = GT_DEFAULT;
7312     getText.codepage      = CP_ACP;
7313     getText.lpDefaultChar = NULL;
7314     getText.lpUsedDefChar = NULL;
7315     buf[0] = 0x00;
7316     result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7317     expected = testenteritems[i].expectedemtext;
7318
7319     resultbuf[0]=0x00;
7320     for (j = 0; j < (UINT)result; j++)
7321       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7322     expectedbuf[0] = '\0';
7323     for (j = 0; j < strlen(expected); j++)
7324       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7325
7326     result = strcmp(expected, buf);
7327     ok (result == 0,
7328         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7329         i, resultbuf, expectedbuf);
7330
7331     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7332     getText.cb = sizeof(buf);
7333     getText.flags = GT_USECRLF;
7334     getText.codepage      = CP_ACP;
7335     getText.lpDefaultChar = NULL;
7336     getText.lpUsedDefChar = NULL;
7337     buf[0] = 0x00;
7338     result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7339     expected = testenteritems[i].expectedemtextcrlf;
7340
7341     resultbuf[0]=0x00;
7342     for (j = 0; j < (UINT)result; j++)
7343       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7344     expectedbuf[0] = '\0';
7345     for (j = 0; j < strlen(expected); j++)
7346       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7347
7348     result = strcmp(expected, buf);
7349     ok (result == 0,
7350         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7351         i, resultbuf, expectedbuf);
7352   }
7353
7354   DestroyWindow(hwndRichEdit);
7355 }
7356
7357 START_TEST( editor )
7358 {
7359   BOOL ret;
7360   /* Must explicitly LoadLibrary(). The test has no references to functions in
7361    * RICHED20.DLL, so the linker doesn't actually link to it. */
7362   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7363   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7364
7365   test_WM_CHAR();
7366   test_EM_FINDTEXT(FALSE);
7367   test_EM_FINDTEXT(TRUE);
7368   test_EM_GETLINE();
7369   test_EM_POSFROMCHAR();
7370   test_EM_SCROLLCARET();
7371   test_EM_SCROLL();
7372   test_scrollbar_visibility();
7373   test_WM_SETTEXT();
7374   test_EM_LINELENGTH();
7375   test_EM_SETCHARFORMAT();
7376   test_EM_SETTEXTMODE();
7377   test_TM_PLAINTEXT();
7378   test_EM_SETOPTIONS();
7379   test_WM_GETTEXT();
7380   test_EM_GETTEXTRANGE();
7381   test_EM_GETSELTEXT();
7382   test_EM_SETUNDOLIMIT();
7383   test_ES_PASSWORD();
7384   test_EM_SETTEXTEX();
7385   test_EM_LIMITTEXT();
7386   test_EM_EXLIMITTEXT();
7387   test_EM_GETLIMITTEXT();
7388   test_WM_SETFONT();
7389   test_EM_GETMODIFY();
7390   test_EM_EXSETSEL();
7391   test_WM_PASTE();
7392   test_EM_STREAMIN();
7393   test_EM_STREAMOUT();
7394   test_EM_STREAMOUT_FONTTBL();
7395   test_EM_StreamIn_Undo();
7396   test_EM_FORMATRANGE();
7397   test_unicode_conversions();
7398   test_EM_GETTEXTLENGTHEX();
7399   test_EM_REPLACESEL(1);
7400   test_EM_REPLACESEL(0);
7401   test_WM_NOTIFY();
7402   test_EM_AUTOURLDETECT();
7403   test_eventMask();
7404   test_undo_coalescing();
7405   test_word_movement();
7406   test_EM_CHARFROMPOS();
7407   test_SETPARAFORMAT();
7408   test_word_wrap();
7409   test_autoscroll();
7410   test_format_rect();
7411   test_WM_GETDLGCODE();
7412   test_zoom();
7413   test_dialogmode();
7414   test_EM_FINDWORDBREAK_W();
7415   test_EM_FINDWORDBREAK_A();
7416   test_enter();
7417
7418   /* Set the environment variable WINETEST_RICHED20 to keep windows
7419    * responsive and open for 30 seconds. This is useful for debugging.
7420    */
7421   if (getenv( "WINETEST_RICHED20" )) {
7422     keep_responsive(30);
7423   }
7424
7425   OleFlushClipboard();
7426   ret = FreeLibrary(hmoduleRichEdit);
7427   ok(ret, "error: %d\n", (int) GetLastError());
7428 }