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