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