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