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