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