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