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