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