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