wbemprox: Add support for enumerating class properties.
[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     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2444     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2445
2446     ok(r == 0x00010000,
2447        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2448     ok(y_before == y_after,
2449        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2450        y_before, y_after);
2451   }
2452   DestroyWindow(hwndRichEdit);
2453 }
2454
2455 static unsigned int recursionLevel = 0;
2456 static unsigned int WM_SIZE_recursionLevel = 0;
2457 static BOOL bailedOutOfRecursion = FALSE;
2458 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2459
2460 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2461 {
2462     LRESULT r;
2463
2464     if (bailedOutOfRecursion) return 0;
2465     if (recursionLevel >= 32) {
2466         bailedOutOfRecursion = TRUE;
2467         return 0;
2468     }
2469
2470     recursionLevel++;
2471     switch (message) {
2472     case WM_SIZE:
2473         WM_SIZE_recursionLevel++;
2474         r = richeditProc(hwnd, message, wParam, lParam);
2475         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2476         ShowScrollBar(hwnd, SB_VERT, TRUE);
2477         WM_SIZE_recursionLevel--;
2478         break;
2479     default:
2480         r = richeditProc(hwnd, message, wParam, lParam);
2481         break;
2482     }
2483     recursionLevel--;
2484     return r;
2485 }
2486
2487 static void test_scrollbar_visibility(void)
2488 {
2489   HWND hwndRichEdit;
2490   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2491   SCROLLINFO si;
2492   WNDCLASSA cls;
2493   BOOL r;
2494
2495   /* These tests show that richedit should temporarily refrain from automatically
2496      hiding or showing its scrollbars (vertical at least) when an explicit request
2497      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2498      Some applications depend on forced showing (when otherwise richedit would
2499      hide the vertical scrollbar) and are thrown on an endless recursive loop
2500      if richedit auto-hides the scrollbar again. Apparently they never heard of
2501      the ES_DISABLENOSCROLL style... */
2502
2503   hwndRichEdit = new_richedit(NULL);
2504
2505   /* Test default scrollbar visibility behavior */
2506   memset(&si, 0, sizeof(si));
2507   si.cbSize = sizeof(si);
2508   si.fMask = SIF_PAGE | SIF_RANGE;
2509   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2510   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2511     "Vertical scrollbar is visible, should be invisible.\n");
2512   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2513         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2514         si.nPage, si.nMin, si.nMax);
2515
2516   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2517   memset(&si, 0, sizeof(si));
2518   si.cbSize = sizeof(si);
2519   si.fMask = SIF_PAGE | SIF_RANGE;
2520   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2521   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2522     "Vertical scrollbar is visible, should be invisible.\n");
2523   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2524         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2525         si.nPage, si.nMin, si.nMax);
2526
2527   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2528   memset(&si, 0, sizeof(si));
2529   si.cbSize = sizeof(si);
2530   si.fMask = SIF_PAGE | SIF_RANGE;
2531   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2532   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2533     "Vertical scrollbar is invisible, should be visible.\n");
2534   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2535         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2536         si.nPage, si.nMin, si.nMax);
2537
2538   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2539      even though it hides the scrollbar */
2540   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2541   memset(&si, 0, sizeof(si));
2542   si.cbSize = sizeof(si);
2543   si.fMask = SIF_PAGE | SIF_RANGE;
2544   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546     "Vertical scrollbar is visible, should be invisible.\n");
2547   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549         si.nPage, si.nMin, si.nMax);
2550
2551   /* Setting non-scrolling text again does *not* reset scrollbar range */
2552   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2553   memset(&si, 0, sizeof(si));
2554   si.cbSize = sizeof(si);
2555   si.fMask = SIF_PAGE | SIF_RANGE;
2556   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2557   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2558     "Vertical scrollbar is visible, should be invisible.\n");
2559   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2560         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2561         si.nPage, si.nMin, si.nMax);
2562
2563   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2564   memset(&si, 0, sizeof(si));
2565   si.cbSize = sizeof(si);
2566   si.fMask = SIF_PAGE | SIF_RANGE;
2567   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2568   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2569     "Vertical scrollbar is visible, should be invisible.\n");
2570   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2571         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2572         si.nPage, si.nMin, si.nMax);
2573
2574   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2575   memset(&si, 0, sizeof(si));
2576   si.cbSize = sizeof(si);
2577   si.fMask = SIF_PAGE | SIF_RANGE;
2578   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2579   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2580     "Vertical scrollbar is visible, should be invisible.\n");
2581   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2582         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2583         si.nPage, si.nMin, si.nMax);
2584
2585   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2586   memset(&si, 0, sizeof(si));
2587   si.cbSize = sizeof(si);
2588   si.fMask = SIF_PAGE | SIF_RANGE;
2589   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2590   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2591     "Vertical scrollbar is visible, should be invisible.\n");
2592   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2593         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2594         si.nPage, si.nMin, si.nMax);
2595
2596   DestroyWindow(hwndRichEdit);
2597
2598   /* Test again, with ES_DISABLENOSCROLL style */
2599   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2600
2601   /* Test default scrollbar visibility behavior */
2602   memset(&si, 0, sizeof(si));
2603   si.cbSize = sizeof(si);
2604   si.fMask = SIF_PAGE | SIF_RANGE;
2605   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2606   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2607     "Vertical scrollbar is invisible, should be visible.\n");
2608   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2609         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2610         si.nPage, si.nMin, si.nMax);
2611
2612   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2613   memset(&si, 0, sizeof(si));
2614   si.cbSize = sizeof(si);
2615   si.fMask = SIF_PAGE | SIF_RANGE;
2616   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2617   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2618     "Vertical scrollbar is invisible, should be visible.\n");
2619   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2620         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2621         si.nPage, si.nMin, si.nMax);
2622
2623   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2624   memset(&si, 0, sizeof(si));
2625   si.cbSize = sizeof(si);
2626   si.fMask = SIF_PAGE | SIF_RANGE;
2627   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629     "Vertical scrollbar is invisible, should be visible.\n");
2630   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631         "reported page/range is %d (%d..%d)\n",
2632         si.nPage, si.nMin, si.nMax);
2633
2634   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2635   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2636   memset(&si, 0, sizeof(si));
2637   si.cbSize = sizeof(si);
2638   si.fMask = SIF_PAGE | SIF_RANGE;
2639   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641     "Vertical scrollbar is invisible, should be visible.\n");
2642   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644         si.nPage, si.nMin, si.nMax);
2645
2646   /* Setting non-scrolling text again does *not* reset scrollbar range */
2647   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2648   memset(&si, 0, sizeof(si));
2649   si.cbSize = sizeof(si);
2650   si.fMask = SIF_PAGE | SIF_RANGE;
2651   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2653     "Vertical scrollbar is invisible, should be visible.\n");
2654   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2655         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2656         si.nPage, si.nMin, si.nMax);
2657
2658   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2659   memset(&si, 0, sizeof(si));
2660   si.cbSize = sizeof(si);
2661   si.fMask = SIF_PAGE | SIF_RANGE;
2662   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2663   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2664     "Vertical scrollbar is invisible, should be visible.\n");
2665   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2666         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2667         si.nPage, si.nMin, si.nMax);
2668
2669   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2670   memset(&si, 0, sizeof(si));
2671   si.cbSize = sizeof(si);
2672   si.fMask = SIF_PAGE | SIF_RANGE;
2673   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2674   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2675     "Vertical scrollbar is invisible, should be visible.\n");
2676   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2677         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2678         si.nPage, si.nMin, si.nMax);
2679
2680   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2681   memset(&si, 0, sizeof(si));
2682   si.cbSize = sizeof(si);
2683   si.fMask = SIF_PAGE | SIF_RANGE;
2684   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2686     "Vertical scrollbar is invisible, should be visible.\n");
2687   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2688         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2689         si.nPage, si.nMin, si.nMax);
2690
2691   DestroyWindow(hwndRichEdit);
2692
2693   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2694   hwndRichEdit = new_richedit(NULL);
2695
2696   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2697   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2698   memset(&si, 0, sizeof(si));
2699   si.cbSize = sizeof(si);
2700   si.fMask = SIF_PAGE | SIF_RANGE;
2701   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2702   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2703     "Vertical scrollbar is invisible, should be visible.\n");
2704   todo_wine {
2705   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2706         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2707         si.nPage, si.nMin, si.nMax);
2708   }
2709
2710   /* Ditto, see above */
2711   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2712   memset(&si, 0, sizeof(si));
2713   si.cbSize = sizeof(si);
2714   si.fMask = SIF_PAGE | SIF_RANGE;
2715   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2716   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2717     "Vertical scrollbar is invisible, should be visible.\n");
2718   todo_wine {
2719   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2720         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2721         si.nPage, si.nMin, si.nMax);
2722   }
2723
2724   /* Ditto, see above */
2725   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2726   memset(&si, 0, sizeof(si));
2727   si.cbSize = sizeof(si);
2728   si.fMask = SIF_PAGE | SIF_RANGE;
2729   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2730   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2731     "Vertical scrollbar is invisible, should be visible.\n");
2732   todo_wine {
2733   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2734         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2735         si.nPage, si.nMin, si.nMax);
2736   }
2737
2738   /* Ditto, see above */
2739   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2740   memset(&si, 0, sizeof(si));
2741   si.cbSize = sizeof(si);
2742   si.fMask = SIF_PAGE | SIF_RANGE;
2743   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2744   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2745     "Vertical scrollbar is invisible, should be visible.\n");
2746   todo_wine {
2747   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2748         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2749         si.nPage, si.nMin, si.nMax);
2750   }
2751
2752   /* Ditto, see above */
2753   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2754   memset(&si, 0, sizeof(si));
2755   si.cbSize = sizeof(si);
2756   si.fMask = SIF_PAGE | SIF_RANGE;
2757   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2758   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2759     "Vertical scrollbar is invisible, should be visible.\n");
2760   todo_wine {
2761   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2762         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2763         si.nPage, si.nMin, si.nMax);
2764   }
2765
2766   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2767   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2768   memset(&si, 0, sizeof(si));
2769   si.cbSize = sizeof(si);
2770   si.fMask = SIF_PAGE | SIF_RANGE;
2771   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773     "Vertical scrollbar is visible, should be invisible.\n");
2774   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776         si.nPage, si.nMin, si.nMax);
2777
2778   DestroyWindow(hwndRichEdit);
2779
2780   hwndRichEdit = new_richedit(NULL);
2781
2782   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2783   memset(&si, 0, sizeof(si));
2784   si.cbSize = sizeof(si);
2785   si.fMask = SIF_PAGE | SIF_RANGE;
2786   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788     "Vertical scrollbar is visible, should be invisible.\n");
2789   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2790         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2791         si.nPage, si.nMin, si.nMax);
2792
2793   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2794   memset(&si, 0, sizeof(si));
2795   si.cbSize = sizeof(si);
2796   si.fMask = SIF_PAGE | SIF_RANGE;
2797   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2798   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2799     "Vertical scrollbar is visible, should be invisible.\n");
2800   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2801         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2802         si.nPage, si.nMin, si.nMax);
2803
2804   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2805   memset(&si, 0, sizeof(si));
2806   si.cbSize = sizeof(si);
2807   si.fMask = SIF_PAGE | SIF_RANGE;
2808   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2809   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2810     "Vertical scrollbar is visible, should be invisible.\n");
2811   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2812         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2813         si.nPage, si.nMin, si.nMax);
2814
2815   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2816   memset(&si, 0, sizeof(si));
2817   si.cbSize = sizeof(si);
2818   si.fMask = SIF_PAGE | SIF_RANGE;
2819   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2821     "Vertical scrollbar is visible, should be invisible.\n");
2822   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2823         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2824         si.nPage, si.nMin, si.nMax);
2825
2826   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827   memset(&si, 0, sizeof(si));
2828   si.cbSize = sizeof(si);
2829   si.fMask = SIF_PAGE | SIF_RANGE;
2830   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2832     "Vertical scrollbar is invisible, should be visible.\n");
2833   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834         "reported page/range is %d (%d..%d)\n",
2835         si.nPage, si.nMin, si.nMax);
2836
2837   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2838   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2839   memset(&si, 0, sizeof(si));
2840   si.cbSize = sizeof(si);
2841   si.fMask = SIF_PAGE | SIF_RANGE;
2842   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2843   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2844     "Vertical scrollbar is visible, should be invisible.\n");
2845   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2846         "reported page/range is %d (%d..%d)\n",
2847         si.nPage, si.nMin, si.nMax);
2848
2849   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2850   memset(&si, 0, sizeof(si));
2851   si.cbSize = sizeof(si);
2852   si.fMask = SIF_PAGE | SIF_RANGE;
2853   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2855     "Vertical scrollbar is visible, should be invisible.\n");
2856   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2857         "reported page/range is %d (%d..%d)\n",
2858         si.nPage, si.nMin, si.nMax);
2859
2860   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2861      EM_SCROLL will make visible any forcefully invisible scrollbar */
2862   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2863   memset(&si, 0, sizeof(si));
2864   si.cbSize = sizeof(si);
2865   si.fMask = SIF_PAGE | SIF_RANGE;
2866   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2868     "Vertical scrollbar is invisible, should be visible.\n");
2869   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2870         "reported page/range is %d (%d..%d)\n",
2871         si.nPage, si.nMin, si.nMax);
2872
2873   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2874   memset(&si, 0, sizeof(si));
2875   si.cbSize = sizeof(si);
2876   si.fMask = SIF_PAGE | SIF_RANGE;
2877   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2879     "Vertical scrollbar is visible, should be invisible.\n");
2880   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881         "reported page/range is %d (%d..%d)\n",
2882         si.nPage, si.nMin, si.nMax);
2883
2884   /* Again, EM_SCROLL, with SB_LINEUP */
2885   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2886   memset(&si, 0, sizeof(si));
2887   si.cbSize = sizeof(si);
2888   si.fMask = SIF_PAGE | SIF_RANGE;
2889   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2890   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2891     "Vertical scrollbar is invisible, should be visible.\n");
2892   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2893         "reported page/range is %d (%d..%d)\n",
2894         si.nPage, si.nMin, si.nMax);
2895
2896   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2897   memset(&si, 0, sizeof(si));
2898   si.cbSize = sizeof(si);
2899   si.fMask = SIF_PAGE | SIF_RANGE;
2900   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2901   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2902     "Vertical scrollbar is visible, should be invisible.\n");
2903   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2904         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2905         si.nPage, si.nMin, si.nMax);
2906
2907   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2908   memset(&si, 0, sizeof(si));
2909   si.cbSize = sizeof(si);
2910   si.fMask = SIF_PAGE | SIF_RANGE;
2911   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913     "Vertical scrollbar is invisible, should be visible.\n");
2914   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2915         "reported page/range is %d (%d..%d)\n",
2916         si.nPage, si.nMin, si.nMax);
2917
2918   DestroyWindow(hwndRichEdit);
2919
2920
2921   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2922   hwndRichEdit = new_richedit(NULL);
2923
2924 #define ENABLE_WS_VSCROLL(hwnd) \
2925     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2926 #define DISABLE_WS_VSCROLL(hwnd) \
2927     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2928
2929   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2930   ENABLE_WS_VSCROLL(hwndRichEdit);
2931   memset(&si, 0, sizeof(si));
2932   si.cbSize = sizeof(si);
2933   si.fMask = SIF_PAGE | SIF_RANGE;
2934   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936     "Vertical scrollbar is invisible, should be visible.\n");
2937   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939         si.nPage, si.nMin, si.nMax);
2940
2941   /* Ditto, see above */
2942   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2943   memset(&si, 0, sizeof(si));
2944   si.cbSize = sizeof(si);
2945   si.fMask = SIF_PAGE | SIF_RANGE;
2946   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948     "Vertical scrollbar is invisible, should be visible.\n");
2949   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951         si.nPage, si.nMin, si.nMax);
2952
2953   /* Ditto, see above */
2954   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2955   memset(&si, 0, sizeof(si));
2956   si.cbSize = sizeof(si);
2957   si.fMask = SIF_PAGE | SIF_RANGE;
2958   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960     "Vertical scrollbar is invisible, should be visible.\n");
2961   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963         si.nPage, si.nMin, si.nMax);
2964
2965   /* Ditto, see above */
2966   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2967   memset(&si, 0, sizeof(si));
2968   si.cbSize = sizeof(si);
2969   si.fMask = SIF_PAGE | SIF_RANGE;
2970   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972     "Vertical scrollbar is invisible, should be visible.\n");
2973   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975         si.nPage, si.nMin, si.nMax);
2976
2977   /* Ditto, see above */
2978   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979   memset(&si, 0, sizeof(si));
2980   si.cbSize = sizeof(si);
2981   si.fMask = SIF_PAGE | SIF_RANGE;
2982   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2984     "Vertical scrollbar is invisible, should be visible.\n");
2985   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2986         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2987         si.nPage, si.nMin, si.nMax);
2988
2989   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2990   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2991   memset(&si, 0, sizeof(si));
2992   si.cbSize = sizeof(si);
2993   si.fMask = SIF_PAGE | SIF_RANGE;
2994   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2996     "Vertical scrollbar is visible, should be invisible.\n");
2997   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2998         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2999         si.nPage, si.nMin, si.nMax);
3000
3001   DestroyWindow(hwndRichEdit);
3002
3003   hwndRichEdit = new_richedit(NULL);
3004
3005   DISABLE_WS_VSCROLL(hwndRichEdit);
3006   memset(&si, 0, sizeof(si));
3007   si.cbSize = sizeof(si);
3008   si.fMask = SIF_PAGE | SIF_RANGE;
3009   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3010   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3011     "Vertical scrollbar is visible, should be invisible.\n");
3012   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3013         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3014         si.nPage, si.nMin, si.nMax);
3015
3016   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3017   memset(&si, 0, sizeof(si));
3018   si.cbSize = sizeof(si);
3019   si.fMask = SIF_PAGE | SIF_RANGE;
3020   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3021   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3022     "Vertical scrollbar is visible, should be invisible.\n");
3023   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3024         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3025         si.nPage, si.nMin, si.nMax);
3026
3027   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3028   memset(&si, 0, sizeof(si));
3029   si.cbSize = sizeof(si);
3030   si.fMask = SIF_PAGE | SIF_RANGE;
3031   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3032   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3033     "Vertical scrollbar is visible, should be invisible.\n");
3034   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3035         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3036         si.nPage, si.nMin, si.nMax);
3037
3038   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3039   memset(&si, 0, sizeof(si));
3040   si.cbSize = sizeof(si);
3041   si.fMask = SIF_PAGE | SIF_RANGE;
3042   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3043   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3044     "Vertical scrollbar is visible, should be invisible.\n");
3045   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3046         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3047         si.nPage, si.nMin, si.nMax);
3048
3049   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3050   memset(&si, 0, sizeof(si));
3051   si.cbSize = sizeof(si);
3052   si.fMask = SIF_PAGE | SIF_RANGE;
3053   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3055     "Vertical scrollbar is invisible, should be visible.\n");
3056   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057         "reported page/range is %d (%d..%d)\n",
3058         si.nPage, si.nMin, si.nMax);
3059
3060   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3061   DISABLE_WS_VSCROLL(hwndRichEdit);
3062   memset(&si, 0, sizeof(si));
3063   si.cbSize = sizeof(si);
3064   si.fMask = SIF_PAGE | SIF_RANGE;
3065   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3066   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3067     "Vertical scrollbar is visible, should be invisible.\n");
3068   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3069         "reported page/range is %d (%d..%d)\n",
3070         si.nPage, si.nMin, si.nMax);
3071
3072   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3073   memset(&si, 0, sizeof(si));
3074   si.cbSize = sizeof(si);
3075   si.fMask = SIF_PAGE | SIF_RANGE;
3076   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3077   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3078     "Vertical scrollbar is visible, should be invisible.\n");
3079   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3080         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3081         si.nPage, si.nMin, si.nMax);
3082
3083   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3084   memset(&si, 0, sizeof(si));
3085   si.cbSize = sizeof(si);
3086   si.fMask = SIF_PAGE | SIF_RANGE;
3087   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3088   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3089     "Vertical scrollbar is invisible, should be visible.\n");
3090   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3091         "reported page/range is %d (%d..%d)\n",
3092         si.nPage, si.nMin, si.nMax);
3093
3094   DISABLE_WS_VSCROLL(hwndRichEdit);
3095   memset(&si, 0, sizeof(si));
3096   si.cbSize = sizeof(si);
3097   si.fMask = SIF_PAGE | SIF_RANGE;
3098   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3099   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3100     "Vertical scrollbar is visible, should be invisible.\n");
3101   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3102         "reported page/range is %d (%d..%d)\n",
3103         si.nPage, si.nMin, si.nMax);
3104
3105   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3106      EM_SCROLL will make visible any forcefully invisible scrollbar */
3107   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3108   memset(&si, 0, sizeof(si));
3109   si.cbSize = sizeof(si);
3110   si.fMask = SIF_PAGE | SIF_RANGE;
3111   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3112   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3113     "Vertical scrollbar is invisible, should be visible.\n");
3114   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3115         "reported page/range is %d (%d..%d)\n",
3116         si.nPage, si.nMin, si.nMax);
3117
3118   DISABLE_WS_VSCROLL(hwndRichEdit);
3119   memset(&si, 0, sizeof(si));
3120   si.cbSize = sizeof(si);
3121   si.fMask = SIF_PAGE | SIF_RANGE;
3122   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3124     "Vertical scrollbar is visible, should be invisible.\n");
3125   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126         "reported page/range is %d (%d..%d)\n",
3127         si.nPage, si.nMin, si.nMax);
3128
3129   /* Again, EM_SCROLL, with SB_LINEUP */
3130   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3131   memset(&si, 0, sizeof(si));
3132   si.cbSize = sizeof(si);
3133   si.fMask = SIF_PAGE | SIF_RANGE;
3134   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3135   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3136     "Vertical scrollbar is invisible, should be visible.\n");
3137   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3138         "reported page/range is %d (%d..%d)\n",
3139         si.nPage, si.nMin, si.nMax);
3140
3141   DestroyWindow(hwndRichEdit);
3142
3143   /* This window proc models what is going on with Corman Lisp 3.0.
3144      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3145      force the scrollbar into visibility. Recursion should NOT happen
3146      as a result of this action.
3147    */
3148   r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3149   if (r) {
3150     richeditProc = cls.lpfnWndProc;
3151     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3152     cls.lpszClassName = "RicheditStupidOverride";
3153     if(!RegisterClassA(&cls)) assert(0);
3154
3155     recursionLevel = 0;
3156     WM_SIZE_recursionLevel = 0;
3157     bailedOutOfRecursion = FALSE;
3158     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3159     ok(!bailedOutOfRecursion,
3160         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3161
3162     recursionLevel = 0;
3163     WM_SIZE_recursionLevel = 0;
3164     bailedOutOfRecursion = FALSE;
3165     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3166     ok(!bailedOutOfRecursion,
3167         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3168
3169     /* Unblock window in order to process WM_DESTROY */
3170     recursionLevel = 0;
3171     bailedOutOfRecursion = FALSE;
3172     WM_SIZE_recursionLevel = 0;
3173     DestroyWindow(hwndRichEdit);
3174   }
3175 }
3176
3177 static void test_EM_SETUNDOLIMIT(void)
3178 {
3179   /* cases we test for:
3180    * default behaviour - limiting at 100 undo's 
3181    * undo disabled - setting a limit of 0
3182    * undo limited -  undo limit set to some to some number, like 2
3183    * bad input - sending a negative number should default to 100 undo's */
3184  
3185   HWND hwndRichEdit = new_richedit(NULL);
3186   CHARRANGE cr;
3187   int i;
3188   int result;
3189   
3190   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3191   cr.cpMin = 0;
3192   cr.cpMax = 1;
3193   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3194     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3195       also, multiple pastes don't combine like WM_CHAR would */
3196   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3197
3198   /* first case - check the default */
3199   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3200   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3201     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3202   for (i=0; i<100; i++) /* Undo 100 of them */
3203     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3204   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3205      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3206
3207   /* second case - cannot undo */
3208   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3209   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
3210   SendMessage(hwndRichEdit,
3211               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3212   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3213      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3214
3215   /* third case - set it to an arbitrary number */
3216   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3217   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
3218   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3219   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3220   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3221   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3222   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3223      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3224   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3225   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3226      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3227   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3228   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3229      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3230   
3231   /* fourth case - setting negative numbers should default to 100 undos */
3232   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3233   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3234   ok (result == 100, 
3235       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3236       
3237   DestroyWindow(hwndRichEdit);
3238 }
3239
3240 static void test_ES_PASSWORD(void)
3241 {
3242   /* This isn't hugely testable, so we're just going to run it through its paces */
3243
3244   HWND hwndRichEdit = new_richedit(NULL);
3245   WCHAR result;
3246
3247   /* First, check the default of a regular control */
3248   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3249   ok (result == 0,
3250         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3251
3252   /* Now, set it to something normal */
3253   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3254   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3255   ok (result == 120,
3256         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3257
3258   /* Now, set it to something odd */
3259   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3260   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3261   ok (result == 1234,
3262         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3263   DestroyWindow(hwndRichEdit);
3264 }
3265
3266 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3267                                          LPBYTE pbBuff,
3268                                          LONG cb,
3269                                          LONG *pcb)
3270 {
3271   char** str = (char**)dwCookie;
3272   *pcb = cb;
3273   if (*pcb > 0) {
3274     memcpy(*str, pbBuff, *pcb);
3275     *str += *pcb;
3276   }
3277   return 0;
3278 }
3279
3280 static void test_WM_SETTEXT(void)
3281 {
3282   HWND hwndRichEdit = new_richedit(NULL);
3283   const char * TestItem1 = "TestSomeText";
3284   const char * TestItem2 = "TestSomeText\r";
3285   const char * TestItem2_after = "TestSomeText\r\n";
3286   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3287   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3288   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3289   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3290   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3291   const char * TestItem5_after = "TestSomeText TestSomeText";
3292   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3293   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3294   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3295   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3296
3297   const char rtftextA[] = "{\\rtf sometext}";
3298   const char urtftextA[] = "{\\urtf sometext}";
3299   const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3300   const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3301   const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3302
3303   char buf[1024] = {0};
3304   WCHAR bufW[1024] = {0};
3305   LRESULT result;
3306
3307   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3308      any solitary \r to be converted to \r\n on return. Properly paired
3309      \r\n are not affected. It also shows that the special sequence \r\r\n
3310      gets converted to a single space.
3311    */
3312
3313 #define TEST_SETTEXT(a, b) \
3314   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3315   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3316   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3317   ok (result == lstrlen(buf), \
3318         "WM_GETTEXT returned %ld instead of expected %u\n", \
3319         result, lstrlen(buf)); \
3320   result = strcmp(b, buf); \
3321   ok(result == 0, \
3322         "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3323
3324   TEST_SETTEXT(TestItem1, TestItem1)
3325   TEST_SETTEXT(TestItem2, TestItem2_after)
3326   TEST_SETTEXT(TestItem3, TestItem3_after)
3327   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3328   TEST_SETTEXT(TestItem4, TestItem4_after)
3329   TEST_SETTEXT(TestItem5, TestItem5_after)
3330   TEST_SETTEXT(TestItem6, TestItem6_after)
3331   TEST_SETTEXT(TestItem7, TestItem7_after)
3332
3333   /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3334   TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3335   TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3336   TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3337   TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3338   DestroyWindow(hwndRichEdit);
3339 #undef TEST_SETTEXT
3340
3341 #define TEST_SETTEXTW(a, b) \
3342   result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3343   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3344   result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3345   ok (result == lstrlenW(bufW), \
3346         "WM_GETTEXT returned %ld instead of expected %u\n", \
3347         result, lstrlenW(bufW)); \
3348   result = lstrcmpW(b, bufW); \
3349   ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3350
3351   hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3352                                ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3353                                0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3354   ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3355   TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3356   TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3357   TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3358   TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3359   DestroyWindow(hwndRichEdit);
3360 #undef TEST_SETTEXTW
3361 }
3362
3363 static void test_EM_STREAMOUT(void)
3364 {
3365   HWND hwndRichEdit = new_richedit(NULL);
3366   int r;
3367   EDITSTREAM es;
3368   char buf[1024] = {0};
3369   char * p;
3370
3371   const char * TestItem1 = "TestSomeText";
3372   const char * TestItem2 = "TestSomeText\r";
3373   const char * TestItem3 = "TestSomeText\r\n";
3374
3375   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3376   p = buf;
3377   es.dwCookie = (DWORD_PTR)&p;
3378   es.dwError = 0;
3379   es.pfnCallback = test_WM_SETTEXT_esCallback;
3380   memset(buf, 0, sizeof(buf));
3381   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3382   r = strlen(buf);
3383   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3384   ok(strcmp(buf, TestItem1) == 0,
3385         "streamed text different, got %s\n", buf);
3386
3387   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3388   p = buf;
3389   es.dwCookie = (DWORD_PTR)&p;
3390   es.dwError = 0;
3391   es.pfnCallback = test_WM_SETTEXT_esCallback;
3392   memset(buf, 0, sizeof(buf));
3393   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3394   r = strlen(buf);
3395   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3396   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3397   ok(strcmp(buf, TestItem3) == 0,
3398         "streamed text different from, got %s\n", buf);
3399   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3400   p = buf;
3401   es.dwCookie = (DWORD_PTR)&p;
3402   es.dwError = 0;
3403   es.pfnCallback = test_WM_SETTEXT_esCallback;
3404   memset(buf, 0, sizeof(buf));
3405   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3406   r = strlen(buf);
3407   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3408   ok(strcmp(buf, TestItem3) == 0,
3409         "streamed text different, got %s\n", buf);
3410
3411   DestroyWindow(hwndRichEdit);
3412 }
3413
3414 static void test_EM_STREAMOUT_FONTTBL(void)
3415 {
3416   HWND hwndRichEdit = new_richedit(NULL);
3417   EDITSTREAM es;
3418   char buf[1024] = {0};
3419   char * p;
3420   char * fontTbl;
3421   int brackCount;
3422
3423   const char * TestItem = "TestSomeText";
3424
3425   /* fills in the richedit control with some text */
3426   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3427
3428   /* streams out the text in rtf format */
3429   p = buf;
3430   es.dwCookie = (DWORD_PTR)&p;
3431   es.dwError = 0;
3432   es.pfnCallback = test_WM_SETTEXT_esCallback;
3433   memset(buf, 0, sizeof(buf));
3434   SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3435
3436   /* scans for \fonttbl, error if not found */
3437   fontTbl = strstr(buf, "\\fonttbl");
3438   ok(fontTbl != NULL, "missing \\fonttbl section\n");
3439   if(fontTbl)
3440   {
3441       /* scans for terminating closing bracket */
3442       brackCount = 1;
3443       while(*fontTbl && brackCount)
3444       {
3445           if(*fontTbl == '{')
3446               brackCount++;
3447           else if(*fontTbl == '}')
3448               brackCount--;
3449           fontTbl++;
3450       }
3451     /* checks whether closing bracket is ok */
3452       ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3453       if(!brackCount)
3454       {
3455           /* char before closing fonttbl block should be a closed bracket */
3456           fontTbl -= 2;
3457           ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3458
3459           /* char after fonttbl block should be a crlf */
3460           fontTbl += 2;
3461           ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3462       }
3463   }
3464   DestroyWindow(hwndRichEdit);
3465 }
3466
3467
3468 static void test_EM_SETTEXTEX(void)
3469 {
3470   HWND hwndRichEdit, parent;
3471   SCROLLINFO si;
3472   int sel_start, sel_end;
3473   SETTEXTEX setText;
3474   GETTEXTEX getText;
3475   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
3476                        'S', 'o', 'm', 'e', 
3477                        'T', 'e', 'x', 't', 0}; 
3478   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3479                           't', 'S', 'o', 'm',
3480                           'e', 'T', 'e', 'x',
3481                           't', 't', 'S', 'o',
3482                           'm', 'e', 'T', 'e',
3483                           'x', 't', 0};
3484   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3485                            '\r','t','S','o','m','e','T','e','x','t',0};
3486   WCHAR TestItem2[] = {'T', 'e', 's', 't',
3487                        'S', 'o', 'm', 'e',
3488                        'T', 'e', 'x', 't',
3489                       '\r', 0};
3490   const char * TestItem2_after = "TestSomeText\r\n";
3491   WCHAR TestItem3[] = {'T', 'e', 's', 't',
3492                        'S', 'o', 'm', 'e',
3493                        'T', 'e', 'x', 't',
3494                       '\r','\n','\r','\n', 0};
3495   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3496                        'S', 'o', 'm', 'e',
3497                        'T', 'e', 'x', 't',
3498                        '\n','\n', 0};
3499   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3500                        'S', 'o', 'm', 'e',
3501                        'T', 'e', 'x', 't',
3502                        '\r','\r', 0};
3503   WCHAR TestItem4[] = {'T', 'e', 's', 't',
3504                        'S', 'o', 'm', 'e',
3505                        'T', 'e', 'x', 't',
3506                       '\r','\r','\n','\r',
3507                       '\n', 0};
3508   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3509                        'S', 'o', 'm', 'e',
3510                        'T', 'e', 'x', 't',
3511                        ' ','\r', 0};
3512 #define MAX_BUF_LEN 1024
3513   WCHAR buf[MAX_BUF_LEN];
3514   char bufACP[MAX_BUF_LEN];
3515   char * p;
3516   int result;
3517   CHARRANGE cr;
3518   EDITSTREAM es;
3519   WNDCLASSA cls;
3520
3521   /* Test the scroll position with and without a parent window.
3522    *
3523    * For some reason the scroll position is 0 after EM_SETTEXTEX
3524    * with the ST_SELECTION flag only when the control has a parent
3525    * window, even though the selection is at the end. */
3526   cls.style = 0;
3527   cls.lpfnWndProc = DefWindowProcA;
3528   cls.cbClsExtra = 0;
3529   cls.cbWndExtra = 0;
3530   cls.hInstance = GetModuleHandleA(0);
3531   cls.hIcon = 0;
3532   cls.hCursor = LoadCursorA(0, IDC_ARROW);
3533   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3534   cls.lpszMenuName = NULL;
3535   cls.lpszClassName = "ParentTestClass";
3536   if(!RegisterClassA(&cls)) assert(0);
3537
3538   parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3539                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
3540   ok (parent != 0, "Failed to create parent window\n");
3541
3542   hwndRichEdit = CreateWindowEx(0,
3543                         RICHEDIT_CLASS, NULL,
3544                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3545                         0, 0, 200, 60, parent, NULL,
3546                         hmoduleRichEdit, NULL);
3547
3548   setText.codepage = CP_ACP;
3549   setText.flags = ST_SELECTION;
3550   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3551               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3552   si.cbSize = sizeof(si);
3553   si.fMask = SIF_ALL;
3554   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3555   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3556   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3557   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3558   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3559
3560   DestroyWindow(parent);
3561
3562   /* Test without a parent window */
3563   hwndRichEdit = new_richedit(NULL);
3564   setText.codepage = CP_ACP;
3565   setText.flags = ST_SELECTION;
3566   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3567               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3568   si.cbSize = sizeof(si);
3569   si.fMask = SIF_ALL;
3570   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3571   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3572   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3573   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3574   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3575
3576   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3577    * but this time it is because the selection is at the beginning. */
3578   setText.codepage = CP_ACP;
3579   setText.flags = ST_DEFAULT;
3580   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3581               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3582   si.cbSize = sizeof(si);
3583   si.fMask = SIF_ALL;
3584   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3585   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3586   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3587   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3588   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3589
3590   setText.codepage = 1200;  /* no constant for unicode */
3591   getText.codepage = 1200;  /* no constant for unicode */
3592   getText.cb = MAX_BUF_LEN;
3593   getText.flags = GT_DEFAULT;
3594   getText.lpDefaultChar = NULL;
3595   getText.lpUsedDefChar = NULL;
3596
3597   setText.flags = 0;
3598   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3599   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3600   ok(lstrcmpW(buf, TestItem1) == 0,
3601       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3602
3603   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3604      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3605    */
3606   setText.codepage = 1200;  /* no constant for unicode */
3607   getText.codepage = 1200;  /* no constant for unicode */
3608   getText.cb = MAX_BUF_LEN;
3609   getText.flags = GT_DEFAULT;
3610   getText.lpDefaultChar = NULL;
3611   getText.lpUsedDefChar = NULL;
3612   setText.flags = 0;
3613   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3614   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3615   ok(lstrcmpW(buf, TestItem2) == 0,
3616       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3617
3618   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3619   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3620   ok(strcmp((const char *)buf, TestItem2_after) == 0,
3621       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3622
3623   /* Baseline test for just-enough buffer space for string */
3624   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3625   getText.codepage = 1200;  /* no constant for unicode */
3626   getText.flags = GT_DEFAULT;
3627   getText.lpDefaultChar = NULL;
3628   getText.lpUsedDefChar = NULL;
3629   memset(buf, 0, MAX_BUF_LEN);
3630   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3631   ok(lstrcmpW(buf, TestItem2) == 0,
3632       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3633
3634   /* When there is enough space for one character, but not both, of the CRLF
3635      pair at the end of the string, the CR is not copied at all. That is,
3636      the caller must not see CRLF pairs truncated to CR at the end of the
3637      string.
3638    */
3639   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3640   getText.codepage = 1200;  /* no constant for unicode */
3641   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
3642   getText.lpDefaultChar = NULL;
3643   getText.lpUsedDefChar = NULL;
3644   memset(buf, 0, MAX_BUF_LEN);
3645   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3646   ok(lstrcmpW(buf, TestItem1) == 0,
3647       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3648
3649
3650   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3651   setText.codepage = 1200;  /* no constant for unicode */
3652   getText.codepage = 1200;  /* no constant for unicode */
3653   getText.cb = MAX_BUF_LEN;
3654   getText.flags = GT_DEFAULT;
3655   getText.lpDefaultChar = NULL;
3656   getText.lpUsedDefChar = NULL;
3657   setText.flags = 0;
3658   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3659   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3660   ok(lstrcmpW(buf, TestItem3_after) == 0,
3661       "EM_SETTEXTEX did not convert properly\n");
3662
3663   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3664   setText.codepage = 1200;  /* no constant for unicode */
3665   getText.codepage = 1200;  /* no constant for unicode */
3666   getText.cb = MAX_BUF_LEN;
3667   getText.flags = GT_DEFAULT;
3668   getText.lpDefaultChar = NULL;
3669   getText.lpUsedDefChar = NULL;
3670   setText.flags = 0;
3671   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3672   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3673   ok(lstrcmpW(buf, TestItem3_after) == 0,
3674       "EM_SETTEXTEX did not convert properly\n");
3675
3676   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3677   setText.codepage = 1200;  /* no constant for unicode */
3678   getText.codepage = 1200;  /* no constant for unicode */
3679   getText.cb = MAX_BUF_LEN;
3680   getText.flags = GT_DEFAULT;
3681   getText.lpDefaultChar = NULL;
3682   getText.lpUsedDefChar = NULL;
3683   setText.flags = 0;
3684   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3685   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3686   ok(lstrcmpW(buf, TestItem4_after) == 0,
3687       "EM_SETTEXTEX did not convert properly\n");
3688
3689   /* !ST_SELECTION && Unicode && !\rtf */
3690   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3691   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3692   
3693   ok (result == 1, 
3694       "EM_SETTEXTEX returned %d, instead of 1\n",result);
3695   ok(lstrlenW(buf) == 0,
3696       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3697   
3698   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3699   setText.flags = 0;
3700   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3701   /* select some text */
3702   cr.cpMax = 1;
3703   cr.cpMin = 3;
3704   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3705   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3706   setText.flags = ST_SELECTION;
3707   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3708   ok(result == 0,
3709       "EM_SETTEXTEX with NULL lParam to replace selection"
3710       " with no text should return 0. Got %i\n",
3711       result);
3712   
3713   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3714   setText.flags = 0;
3715   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3716   /* select some text */
3717   cr.cpMax = 1;
3718   cr.cpMin = 3;
3719   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3720   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3721   setText.flags = ST_SELECTION;
3722   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3723                        (WPARAM)&setText, (LPARAM) TestItem1);
3724   /* get text */
3725   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3726   ok(result == lstrlenW(TestItem1),
3727       "EM_SETTEXTEX with NULL lParam to replace selection"
3728       " with no text should return 0. Got %i\n",
3729       result);
3730   ok(lstrlenW(buf) == 22,
3731       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3732       lstrlenW(buf) );
3733
3734   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3735   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3736   p = (char *)buf;
3737   es.dwCookie = (DWORD_PTR)&p;
3738   es.dwError = 0;
3739   es.pfnCallback = test_WM_SETTEXT_esCallback;
3740   memset(buf, 0, sizeof(buf));
3741   SendMessage(hwndRichEdit, EM_STREAMOUT,
3742               (WPARAM)(SF_RTF), (LPARAM)&es);
3743   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3744
3745   /* !ST_SELECTION && !Unicode && \rtf */
3746   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3747   getText.codepage = 1200;  /* no constant for unicode */
3748   getText.cb = MAX_BUF_LEN;
3749   getText.flags = GT_DEFAULT;
3750   getText.lpDefaultChar = NULL;
3751   getText.lpUsedDefChar = NULL;
3752
3753   setText.flags = 0;
3754   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3755   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3756   ok(lstrcmpW(buf, TestItem1) == 0,
3757       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3758
3759   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3760    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3761   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3762   getText.codepage = CP_ACP;
3763   getText.cb = MAX_BUF_LEN;
3764   getText.flags = GT_DEFAULT;
3765   getText.lpDefaultChar = NULL;
3766   getText.lpUsedDefChar = NULL;
3767
3768   setText.flags = ST_SELECTION;
3769   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3770   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3771   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3772   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3773   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3774
3775   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3776   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3777   p = (char *)buf;
3778   es.dwCookie = (DWORD_PTR)&p;
3779   es.dwError = 0;
3780   es.pfnCallback = test_WM_SETTEXT_esCallback;
3781   memset(buf, 0, sizeof(buf));
3782   SendMessage(hwndRichEdit, EM_STREAMOUT,
3783               (WPARAM)(SF_RTF), (LPARAM)&es);
3784   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3785
3786   /* select some text */
3787   cr.cpMax = 1;
3788   cr.cpMin = 3;
3789   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3790
3791   /* ST_SELECTION && !Unicode && \rtf */
3792   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3793   getText.codepage = 1200;  /* no constant for unicode */
3794   getText.cb = MAX_BUF_LEN;
3795   getText.flags = GT_DEFAULT;
3796   getText.lpDefaultChar = NULL;
3797   getText.lpUsedDefChar = NULL;
3798
3799   setText.flags = ST_SELECTION;
3800   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3801   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3802   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3803
3804   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3805   setText.codepage = 1200;  /* no constant for unicode */
3806   getText.codepage = CP_ACP;
3807   getText.cb = MAX_BUF_LEN;
3808
3809   setText.flags = 0;
3810   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3811   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3812
3813   /* select some text */
3814   cr.cpMax = 1;
3815   cr.cpMin = 3;
3816   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3817
3818   /* ST_SELECTION && !Unicode && !\rtf */
3819   setText.codepage = CP_ACP;
3820   getText.codepage = 1200;  /* no constant for unicode */
3821   getText.cb = MAX_BUF_LEN;
3822   getText.flags = GT_DEFAULT;
3823   getText.lpDefaultChar = NULL;
3824   getText.lpUsedDefChar = NULL;
3825
3826   setText.flags = ST_SELECTION;
3827   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3828   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3829   ok(lstrcmpW(buf, TestItem1alt) == 0,
3830       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3831       " using ST_SELECTION and non-Unicode\n");
3832
3833   /* Test setting text using rich text format */
3834   setText.flags = 0;
3835   setText.codepage = CP_ACP;
3836   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3837   getText.codepage = CP_ACP;
3838   getText.cb = MAX_BUF_LEN;
3839   getText.flags = GT_DEFAULT;
3840   getText.lpDefaultChar = NULL;
3841   getText.lpUsedDefChar = NULL;
3842   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3843   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3844
3845   setText.flags = 0;
3846   setText.codepage = CP_ACP;
3847   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3848   getText.codepage = CP_ACP;
3849   getText.cb = MAX_BUF_LEN;
3850   getText.flags = GT_DEFAULT;
3851   getText.lpDefaultChar = NULL;
3852   getText.lpUsedDefChar = NULL;
3853   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3854   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3855
3856   DestroyWindow(hwndRichEdit);
3857 }
3858
3859 static void test_EM_LIMITTEXT(void)
3860 {
3861   int ret;
3862
3863   HWND hwndRichEdit = new_richedit(NULL);
3864
3865   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3866    * about setting the length to -1 for multiline edit controls doesn't happen.
3867    */
3868
3869   /* Don't check default gettextlimit case. That's done in other tests */
3870
3871   /* Set textlimit to 100 */
3872   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3873   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3874   ok (ret == 100,
3875       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3876
3877   /* Set textlimit to 0 */
3878   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3879   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3880   ok (ret == 65536,
3881       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3882
3883   /* Set textlimit to -1 */
3884   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3885   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3886   ok (ret == -1,
3887       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3888
3889   /* Set textlimit to -2 */
3890   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3891   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3892   ok (ret == -2,
3893       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3894
3895   DestroyWindow (hwndRichEdit);
3896 }
3897
3898
3899 static void test_EM_EXLIMITTEXT(void)
3900 {
3901   int i, selBegin, selEnd, len1, len2;
3902   int result;
3903   char text[1024 + 1];
3904   char buffer[1024 + 1];
3905   int textlimit = 0; /* multiple of 100 */
3906   HWND hwndRichEdit = new_richedit(NULL);
3907   
3908   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3909   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3910   
3911   textlimit = 256000;
3912   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3913   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3914   /* set higher */
3915   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3916   
3917   textlimit = 1000;
3918   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3919   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3920   /* set lower */
3921   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3922  
3923   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3924   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3925   /* default for WParam = 0 */
3926   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3927  
3928   textlimit = sizeof(text)-1;
3929   memset(text, 'W', textlimit);
3930   text[sizeof(text)-1] = 0;
3931   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3932   /* maxed out text */
3933   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3934   
3935   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
3936   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3937   len1 = selEnd - selBegin;
3938   
3939   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3940   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3941   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3942   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3943   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3944   len2 = selEnd - selBegin;
3945   
3946   ok(len1 != len2,
3947     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3948     len1,len2,i);
3949   
3950   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3951   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3952   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3953   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3954   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3955   len1 = selEnd - selBegin;
3956   
3957   ok(len1 != len2,
3958     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3959     len1,len2,i);
3960   
3961   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3962   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3963   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
3964   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3965   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3966   len2 = selEnd - selBegin;
3967   
3968   ok(len1 == len2, 
3969     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3970     len1,len2,i);
3971
3972   /* set text up to the limit, select all the text, then add a char */
3973   textlimit = 5;
3974   memset(text, 'W', textlimit);
3975   text[textlimit] = 0;
3976   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3977   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3978   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3979   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3980   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3981   result = strcmp(buffer, "A");
3982   ok(0 == result, "got string = \"%s\"\n", buffer);
3983
3984   /* WM_SETTEXT not limited */
3985   textlimit = 10;
3986   memset(text, 'W', textlimit);
3987   text[textlimit] = 0;
3988   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3989   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3990   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3991   i = strlen(buffer);
3992   ok(10 == i, "expected 10 chars\n");
3993   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3994   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3995
3996   /* try inserting more text at end */
3997   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3998   ok(0 == i, "WM_CHAR wasn't processed\n");
3999   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4000   i = strlen(buffer);
4001   ok(10 == i, "expected 10 chars, got %i\n", i);
4002   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4003   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4004
4005   /* try inserting text at beginning */
4006   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4007   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4008   ok(0 == i, "WM_CHAR wasn't processed\n");
4009   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4010   i = strlen(buffer);
4011   ok(10 == i, "expected 10 chars, got %i\n", i);
4012   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4013   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4014
4015   /* WM_CHAR is limited */
4016   textlimit = 1;
4017   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4018   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
4019   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4020   ok(0 == i, "WM_CHAR wasn't processed\n");
4021   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4022   ok(0 == i, "WM_CHAR wasn't processed\n");
4023   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4024   i = strlen(buffer);
4025   ok(1 == i, "expected 1 chars, got %i instead\n", i);
4026
4027   DestroyWindow(hwndRichEdit);
4028 }
4029
4030 static void test_EM_GETLIMITTEXT(void)
4031 {
4032   int i;
4033   HWND hwndRichEdit = new_richedit(NULL);
4034
4035   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4036   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4037
4038   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4039   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4040   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4041
4042   DestroyWindow(hwndRichEdit);
4043 }
4044
4045 static void test_WM_SETFONT(void)
4046 {
4047   /* There is no invalid input or error conditions for this function.
4048    * NULL wParam and lParam just fall back to their default values 
4049    * It should be noted that even if you use a gibberish name for your fonts
4050    * here, it will still work because the name is stored. They will display as
4051    * System, but will report their name to be whatever they were created as */
4052   
4053   HWND hwndRichEdit = new_richedit(NULL);
4054   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4055     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4056     FF_DONTCARE, "Marlett");
4057   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4058     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4059     FF_DONTCARE, "MS Sans Serif");
4060   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4061     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4062     FF_DONTCARE, "Courier");
4063   LOGFONTA sentLogFont;
4064   CHARFORMAT2A returnedCF2A;
4065   
4066   returnedCF2A.cbSize = sizeof(returnedCF2A);
4067   
4068   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4069   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4070   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4071
4072   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4073   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4074     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4075     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4076
4077   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4078   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4079   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4080   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4081     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4082     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4083     
4084   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4085   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4086   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4087   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4088     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4089     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4090    
4091   /* This last test is special since we send in NULL. We clear the variables
4092    * and just compare to "System" instead of the sent in font name. */
4093   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4094   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4095   returnedCF2A.cbSize = sizeof(returnedCF2A);
4096   
4097   SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4098   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4099   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4100   ok (!strcmp("System",returnedCF2A.szFaceName),
4101     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4102   
4103   DestroyWindow(hwndRichEdit);
4104 }
4105
4106
4107 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4108                                          LPBYTE pbBuff,
4109                                          LONG cb,
4110                                          LONG *pcb)
4111 {
4112   const char** str = (const char**)dwCookie;
4113   int size = strlen(*str);
4114   if(size > 3)  /* let's make it piecemeal for fun */
4115     size = 3;
4116   *pcb = cb;
4117   if (*pcb > size) {
4118     *pcb = size;
4119   }
4120   if (*pcb > 0) {
4121     memcpy(pbBuff, *str, *pcb);
4122     *str += *pcb;
4123   }
4124   return 0;
4125 }
4126
4127 static void test_EM_GETMODIFY(void)
4128 {
4129   HWND hwndRichEdit = new_richedit(NULL);
4130   LRESULT result;
4131   SETTEXTEX setText;
4132   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
4133                        'S', 'o', 'm', 'e', 
4134                        'T', 'e', 'x', 't', 0}; 
4135   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
4136                        'S', 'o', 'm', 'e', 
4137                        'O', 't', 'h', 'e', 'r',
4138                        'T', 'e', 'x', 't', 0}; 
4139   const char* streamText = "hello world";
4140   CHARFORMAT2 cf2;
4141   PARAFORMAT2 pf2;
4142   EDITSTREAM es;
4143   
4144   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4145     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4146     FF_DONTCARE, "Courier");
4147   
4148   setText.codepage = 1200;  /* no constant for unicode */
4149   setText.flags = ST_KEEPUNDO;
4150   
4151
4152   /* modify flag shouldn't be set when richedit is first created */
4153   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4154   ok (result == 0, 
4155       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4156   
4157   /* setting modify flag should actually set it */
4158   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4159   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4160   ok (result != 0, 
4161       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4162   
4163   /* clearing modify flag should actually clear it */
4164   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4165   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4166   ok (result == 0, 
4167       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4168  
4169   /* setting font doesn't change modify flag */
4170   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4171   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4172   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4173   ok (result == 0,
4174       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4175
4176   /* setting text should set modify flag */
4177   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4178   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4179   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4180   ok (result != 0,
4181       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4182   
4183   /* undo previous text doesn't reset modify flag */
4184   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4185   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4186   ok (result != 0,
4187       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4188   
4189   /* set text with no flag to keep undo stack should not set modify flag */
4190   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4191   setText.flags = 0;
4192   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4193   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4194   ok (result == 0,
4195       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4196   
4197   /* WM_SETTEXT doesn't modify */
4198   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4199   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4200   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4201   ok (result == 0,
4202       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4203   
4204   /* clear the text */
4205   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4206   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4207   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4208   ok (result == 0,
4209       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4210   
4211   /* replace text */
4212   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4213   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4214   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4215   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4216   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4217   ok (result != 0,
4218       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4219   
4220   /* copy/paste text 1 */
4221   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4222   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4223   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4224   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4225   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226   ok (result != 0,
4227       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4228   
4229   /* copy/paste text 2 */
4230   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4231   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4232   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4233   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4234   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4235   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4236   ok (result != 0,
4237       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4238   
4239   /* press char */
4240   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4241   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4242   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4243   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4244   ok (result != 0,
4245       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4246
4247   /* press del */
4248   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4249   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4250   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4251   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4252   ok (result != 0,
4253       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4254   
4255   /* set char format */
4256   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4257   cf2.cbSize = sizeof(CHARFORMAT2);
4258   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4259   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4260   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4261   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4262   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4263   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4264   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4265   ok (result != 0,
4266       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4267   
4268   /* set para format */
4269   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4270   pf2.cbSize = sizeof(PARAFORMAT2);
4271   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4272              (LPARAM) &pf2);
4273   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4274   pf2.wAlignment = PFA_RIGHT;
4275   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4276   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277   ok (result == 0,
4278       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4279
4280   /* EM_STREAM */
4281   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4282   es.dwCookie = (DWORD_PTR)&streamText;
4283   es.dwError = 0;
4284   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4285   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4286   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4287   ok (result != 0,
4288       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4289
4290   DestroyWindow(hwndRichEdit);
4291 }
4292
4293 struct exsetsel_s {
4294   LONG min;
4295   LONG max;
4296   LRESULT expected_retval;
4297   int expected_getsel_start;
4298   int expected_getsel_end;
4299   int _getsel_todo_wine;
4300 };
4301
4302 const struct exsetsel_s exsetsel_tests[] = {
4303   /* sanity tests */
4304   {5, 10, 10, 5, 10, 0},
4305   {15, 17, 17, 15, 17, 0},
4306   /* test cpMax > strlen() */
4307   {0, 100, 18, 0, 18, 1},
4308   /* test cpMin == cpMax */
4309   {5, 5, 5, 5, 5, 0},
4310   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4311   {-1, 0, 5, 5, 5, 0},
4312   {-1, 17, 5, 5, 5, 0},
4313   {-1, 18, 5, 5, 5, 0},
4314   /* test cpMin < 0 && cpMax < 0 */
4315   {-1, -1, 17, 17, 17, 0},
4316   {-4, -5, 17, 17, 17, 0},
4317   /* test cMin >=0 && cpMax < 0 (bug 6814) */
4318   {0, -1, 18, 0, 18, 1},
4319   {17, -5, 18, 17, 18, 1},
4320   {18, -3, 17, 17, 17, 0},
4321   /* test if cpMin > cpMax */
4322   {15, 19, 18, 15, 18, 1},
4323   {19, 15, 18, 15, 18, 1}
4324 };
4325
4326 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4327     CHARRANGE cr;
4328     LRESULT result;
4329     int start, end;
4330
4331     cr.cpMin = setsel->min;
4332     cr.cpMax = setsel->max;
4333     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4334
4335     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4336
4337     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4338
4339     if (setsel->_getsel_todo_wine) {
4340         todo_wine {
4341             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);
4342         }
4343     } else {
4344         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);
4345     }
4346 }
4347
4348 static void test_EM_EXSETSEL(void)
4349 {
4350     HWND hwndRichEdit = new_richedit(NULL);
4351     int i;
4352     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4353
4354     /* sending some text to the window */
4355     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4356     /*                                                 01234567890123456*/
4357     /*                                                          10      */
4358
4359     for (i = 0; i < num_tests; i++) {
4360         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4361     }
4362
4363     DestroyWindow(hwndRichEdit);
4364 }
4365
4366 static void test_EM_REPLACESEL(int redraw)
4367 {
4368     HWND hwndRichEdit = new_richedit(NULL);
4369     char buffer[1024] = {0};
4370     int r;
4371     GETTEXTEX getText;
4372     CHARRANGE cr;
4373
4374     /* sending some text to the window */
4375     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4376     /*                                                 01234567890123456*/
4377     /*                                                          10      */
4378
4379     /* FIXME add more tests */
4380     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4381     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4382     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4383     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4384     r = strcmp(buffer, "testing");
4385     ok(0 == r, "expected %d, got %d\n", 0, r);
4386
4387     DestroyWindow(hwndRichEdit);
4388
4389     hwndRichEdit = new_richedit(NULL);
4390
4391     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4392     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4393
4394     /* Test behavior with carriage returns and newlines */
4395     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4396     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4397     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4398     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4399     r = strcmp(buffer, "RichEdit1");
4400     ok(0 == r, "expected %d, got %d\n", 0, r);
4401     getText.cb = 1024;
4402     getText.codepage = CP_ACP;
4403     getText.flags = GT_DEFAULT;
4404     getText.lpDefaultChar = NULL;
4405     getText.lpUsedDefChar = NULL;
4406     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4407     ok(strcmp(buffer, "RichEdit1") == 0,
4408       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4409
4410     /* Test number of lines reported after EM_REPLACESEL */
4411     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4412     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4413
4414     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4415     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4416     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4417     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4418     r = strcmp(buffer, "RichEdit1\r\n");
4419     ok(0 == r, "expected %d, got %d\n", 0, r);
4420     getText.cb = 1024;
4421     getText.codepage = CP_ACP;
4422     getText.flags = GT_DEFAULT;
4423     getText.lpDefaultChar = NULL;
4424     getText.lpUsedDefChar = NULL;
4425     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4426     ok(strcmp(buffer, "RichEdit1\r") == 0,
4427       "EM_GETTEXTEX returned incorrect string\n");
4428
4429     /* Test number of lines reported after EM_REPLACESEL */
4430     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4431     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4432
4433     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4434     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4435     ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4436
4437     /* Test number of lines reported after EM_REPLACESEL */
4438     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4439     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4440
4441     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4442     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4443     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4444     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4445
4446     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4447     r = strcmp(buffer, "RichEdit1\r\n");
4448     ok(0 == r, "expected %d, got %d\n", 0, r);
4449     getText.cb = 1024;
4450     getText.codepage = CP_ACP;
4451     getText.flags = GT_DEFAULT;
4452     getText.lpDefaultChar = NULL;
4453     getText.lpUsedDefChar = NULL;
4454     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4455     ok(strcmp(buffer, "RichEdit1\r") == 0,
4456       "EM_GETTEXTEX returned incorrect string\n");
4457
4458     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4459     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4460     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4461     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4462
4463     /* The following tests show that richedit should handle the special \r\r\n
4464        sequence by turning it into a single space on insertion. However,
4465        EM_REPLACESEL on WinXP returns the number of characters in the original
4466        string.
4467      */
4468
4469     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4470     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4471     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4472     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4473     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4474     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4475     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4476
4477     /* Test the actual string */
4478     getText.cb = 1024;
4479     getText.codepage = CP_ACP;
4480     getText.flags = GT_DEFAULT;
4481     getText.lpDefaultChar = NULL;
4482     getText.lpUsedDefChar = NULL;
4483     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4484     ok(strcmp(buffer, "\r\r") == 0,
4485       "EM_GETTEXTEX returned incorrect string\n");
4486
4487     /* Test number of lines reported after EM_REPLACESEL */
4488     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4489     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4490
4491     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4492     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4493     ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4494     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4495     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4496     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4497     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4498
4499     /* Test the actual string */
4500     getText.cb = 1024;
4501     getText.codepage = CP_ACP;
4502     getText.flags = GT_DEFAULT;
4503     getText.lpDefaultChar = NULL;
4504     getText.lpUsedDefChar = NULL;
4505     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4506     ok(strcmp(buffer, " ") == 0,
4507       "EM_GETTEXTEX returned incorrect string\n");
4508
4509     /* Test number of lines reported after EM_REPLACESEL */
4510     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4511     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4512
4513     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4514     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4515     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4516     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4517     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4518     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4519     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4520
4521     /* Test the actual string */
4522     getText.cb = 1024;
4523     getText.codepage = CP_ACP;
4524     getText.flags = GT_DEFAULT;
4525     getText.lpDefaultChar = NULL;
4526     getText.lpUsedDefChar = NULL;
4527     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4528     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4529       "EM_GETTEXTEX returned incorrect string\n");
4530
4531     /* Test number of lines reported after EM_REPLACESEL */
4532     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4533     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4534
4535     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4536     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4537     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4538     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4539     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4540     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4541     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4542
4543     /* Test the actual string */
4544     getText.cb = 1024;
4545     getText.codepage = CP_ACP;
4546     getText.flags = GT_DEFAULT;
4547     getText.lpDefaultChar = NULL;
4548     getText.lpUsedDefChar = NULL;
4549     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4550     ok(strcmp(buffer, " \r") == 0,
4551       "EM_GETTEXTEX returned incorrect string\n");
4552
4553     /* Test number of lines reported after EM_REPLACESEL */
4554     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4555     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4556
4557     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4558     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4559     ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4560     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4561     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4562     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4563     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4564
4565     /* Test the actual string */
4566     getText.cb = 1024;
4567     getText.codepage = CP_ACP;
4568     getText.flags = GT_DEFAULT;
4569     getText.lpDefaultChar = NULL;
4570     getText.lpUsedDefChar = NULL;
4571     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4572     ok(strcmp(buffer, " \r\r") == 0,
4573       "EM_GETTEXTEX returned incorrect string\n");
4574
4575     /* Test number of lines reported after EM_REPLACESEL */
4576     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4577     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4578
4579     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4580     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4581     ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4582     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4583     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4584     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4585     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4586
4587     /* Test the actual string */
4588     getText.cb = 1024;
4589     getText.codepage = CP_ACP;
4590     getText.flags = GT_DEFAULT;
4591     getText.lpDefaultChar = NULL;
4592     getText.lpUsedDefChar = NULL;
4593     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4594     ok(strcmp(buffer, "\rX\r\r\r") == 0,
4595       "EM_GETTEXTEX returned incorrect string\n");
4596
4597     /* Test number of lines reported after EM_REPLACESEL */
4598     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4599     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4600
4601     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4602     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4603     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4604     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4605     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4606     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4607     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4608
4609     /* Test the actual string */
4610     getText.cb = 1024;
4611     getText.codepage = CP_ACP;
4612     getText.flags = GT_DEFAULT;
4613     getText.lpDefaultChar = NULL;
4614     getText.lpUsedDefChar = NULL;
4615     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4616     ok(strcmp(buffer, "\r\r") == 0,
4617       "EM_GETTEXTEX returned incorrect string\n");
4618
4619     /* Test number of lines reported after EM_REPLACESEL */
4620     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4621     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4622
4623     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4624     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4625     ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4626     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4627     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4628     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4629     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4630
4631     /* Test the actual string */
4632     getText.cb = 1024;
4633     getText.codepage = CP_ACP;
4634     getText.flags = GT_DEFAULT;
4635     getText.lpDefaultChar = NULL;
4636     getText.lpUsedDefChar = NULL;
4637     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4638     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4639       "EM_GETTEXTEX returned incorrect string\n");
4640
4641     /* Test number of lines reported after EM_REPLACESEL */
4642     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4643     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4644
4645     if (!redraw)
4646         /* This is needed to avoid interferring with keybd_event calls
4647          * on other tests that simulate keyboard events. */
4648         SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4649
4650     DestroyWindow(hwndRichEdit);
4651 }
4652
4653 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4654  * to test the state of the modifiers (Ctrl/Alt/Shift).
4655  *
4656  * Therefore Ctrl-<key> keystrokes need to be simulated with
4657  * keybd_event or by using SetKeyboardState to set the modifiers
4658  * and SendMessage to simulate the keystrokes.
4659  */
4660 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4661 {
4662     LRESULT result;
4663     hold_key(VK_CONTROL);
4664     result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4665     release_key(VK_CONTROL);
4666     return result;
4667 }
4668
4669 static void test_WM_PASTE(void)
4670 {
4671     int result;
4672     char buffer[1024] = {0};
4673     const char* text1 = "testing paste\r";
4674     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4675     const char* text1_after = "testing paste\r\n";
4676     const char* text2 = "testing paste\r\rtesting paste";
4677     const char* text2_after = "testing paste\r\n\r\ntesting paste";
4678     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4679     HWND hwndRichEdit = new_richedit(NULL);
4680
4681     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4682     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4683
4684     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
4685     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4686     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
4687     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4688     /* Pasted text should be visible at this step */
4689     result = strcmp(text1_step1, buffer);
4690     ok(result == 0,
4691         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4692
4693     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
4694     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4695     /* Text should be the same as before (except for \r -> \r\n conversion) */
4696     result = strcmp(text1_after, buffer);
4697     ok(result == 0,
4698         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4699
4700     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4701     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4702     send_ctrl_key(hwndRichEdit, 'C');   /* Copy */
4703     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4704     send_ctrl_key(hwndRichEdit, 'V');   /* Paste */
4705     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4706     /* Pasted text should be visible at this step */
4707     result = strcmp(text3, buffer);
4708     ok(result == 0,
4709         "test paste: strcmp = %i\n", result);
4710     send_ctrl_key(hwndRichEdit, 'Z');   /* Undo */
4711     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4712     /* Text should be the same as before (except for \r -> \r\n conversion) */
4713     result = strcmp(text2_after, buffer);
4714     ok(result == 0,
4715         "test paste: strcmp = %i\n", result);
4716     send_ctrl_key(hwndRichEdit, 'Y');   /* Redo */
4717     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718     /* Text should revert to post-paste state */
4719     result = strcmp(buffer,text3);
4720     ok(result == 0,
4721         "test paste: strcmp = %i\n", result);
4722
4723     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4724     /* Send WM_CHAR to simulates Ctrl-V */
4725     SendMessage(hwndRichEdit, WM_CHAR, 22,
4726                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4727     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4728     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4729     result = strcmp(buffer,"");
4730     ok(result == 0,
4731         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4732
4733     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4734      * with SetKeyboard state. */
4735
4736     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4737     /* Simulates paste (Ctrl-V) */
4738     hold_key(VK_CONTROL);
4739     SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4740                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4741     release_key(VK_CONTROL);
4742     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4743     result = strcmp(buffer,"paste");
4744     ok(result == 0,
4745         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4746
4747     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4748     SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4749     /* Simulates copy (Ctrl-C) */
4750     hold_key(VK_CONTROL);
4751     SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4752                 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4753     release_key(VK_CONTROL);
4754     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4755     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4756     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4757     result = strcmp(buffer,"testing");
4758     ok(result == 0,
4759         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4760
4761     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4762     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4763     /* Simulates select all (Ctrl-A) */
4764     hold_key(VK_CONTROL);
4765     SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4766                 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4767     /* Simulates select cut (Ctrl-X) */
4768     SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4769                 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4770     release_key(VK_CONTROL);
4771     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772     result = strcmp(buffer,"");
4773     ok(result == 0,
4774         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4775     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4776     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4777     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4778     result = strcmp(buffer,"cut\r\n");
4779     todo_wine ok(result == 0,
4780         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4781     /* Simulates undo (Ctrl-Z) */
4782     hold_key(VK_CONTROL);
4783     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4784                 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4785     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4786     result = strcmp(buffer,"");
4787     ok(result == 0,
4788         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4789     /* Simulates redo (Ctrl-Y) */
4790     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4791                 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4792     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4793     result = strcmp(buffer,"cut\r\n");
4794     todo_wine ok(result == 0,
4795         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4796     release_key(VK_CONTROL);
4797
4798     DestroyWindow(hwndRichEdit);
4799 }
4800
4801 static void test_EM_FORMATRANGE(void)
4802 {
4803   int r, i, tpp_x, tpp_y;
4804   HDC hdc;
4805   HWND hwndRichEdit = new_richedit(NULL);
4806   FORMATRANGE fr;
4807   BOOL skip_non_english;
4808   static const struct {
4809     const char *string; /* The string */
4810     int first;          /* First 'pagebreak', 0 for don't care */
4811     int second;         /* Second 'pagebreak', 0 for don't care */
4812   } fmtstrings[] = {
4813     {"WINE wine", 0, 0},
4814     {"WINE wineWine", 0, 0},
4815     {"WINE\r\nwine\r\nwine", 5, 10},
4816     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4817     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4818   };
4819
4820   skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4821   if (skip_non_english)
4822     skip("Skipping some tests on non-English platform\n");
4823
4824   hdc = GetDC(hwndRichEdit);
4825   ok(hdc != NULL, "Could not get HDC\n");
4826
4827   /* Calculate the twips per pixel */
4828   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4829   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4830
4831   /* Test the simple case where all the text fits in the page rect. */
4832   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4833   fr.hdc = fr.hdcTarget = hdc;
4834   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4835   fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4836   fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4837   fr.chrg.cpMin = 0;
4838   fr.chrg.cpMax = -1;
4839   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4840   todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4841
4842   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4843   fr.rc.bottom = fr.rcPage.bottom;
4844   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4845   todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4846
4847   SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4848
4849   for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4850   {
4851     GETTEXTLENGTHEX gtl;
4852     SIZE stringsize;
4853     int len;
4854
4855     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4856
4857     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4858     gtl.codepage = CP_ACP;
4859     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4860
4861     /* Get some size information for the string */
4862     GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4863
4864     /* Define the box to be half the width needed and a bit larger than the height.
4865      * Changes to the width means we have at least 2 pages. Changes to the height
4866      * is done so we can check the changing of fr.rc.bottom.
4867      */
4868     fr.hdc = fr.hdcTarget = hdc;
4869     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4870     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4871     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4872
4873     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4874     todo_wine {
4875     ok(r == len, "Expected %d, got %d\n", len, r);
4876     }
4877
4878     /* We know that the page can't hold the full string. See how many characters
4879      * are on the first one
4880      */
4881     fr.chrg.cpMin = 0;
4882     fr.chrg.cpMax = -1;
4883     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4884     todo_wine {
4885     if (! skip_non_english)
4886       ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4887     }
4888     if (fmtstrings[i].first)
4889       todo_wine {
4890       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4891       }
4892     else
4893       ok(r < len, "Expected < %d, got %d\n", len, r);
4894
4895     /* Do another page */
4896     fr.chrg.cpMin = r;
4897     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4898     if (fmtstrings[i].second)
4899       todo_wine {
4900       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4901       }
4902     else if (! skip_non_english)
4903       ok (r < len, "Expected < %d, got %d\n", len, r);
4904
4905     /* There is at least on more page, but we don't care */
4906
4907     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4908     todo_wine {
4909     ok(r == len, "Expected %d, got %d\n", len, r);
4910     }
4911   }
4912
4913   ReleaseDC(NULL, hdc);
4914   DestroyWindow(hwndRichEdit);
4915 }
4916
4917 static int nCallbackCount = 0;
4918
4919 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4920                                  LONG cb, LONG* pcb)
4921 {
4922   const char text[] = {'t','e','s','t'};
4923
4924   if (sizeof(text) <= cb)
4925   {
4926     if ((int)dwCookie != nCallbackCount)
4927     {
4928       *pcb = 0;
4929       return 0;
4930     }
4931
4932     memcpy (pbBuff, text, sizeof(text));
4933     *pcb = sizeof(text);
4934
4935     nCallbackCount++;
4936
4937     return 0;
4938   }
4939   else
4940     return 1; /* indicates callback failed */
4941 }
4942
4943 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4944                                          LPBYTE pbBuff,
4945                                          LONG cb,
4946                                          LONG *pcb)
4947 {
4948   const char** str = (const char**)dwCookie;
4949   int size = strlen(*str);
4950   *pcb = cb;
4951   if (*pcb > size) {
4952     *pcb = size;
4953   }
4954   if (*pcb > 0) {
4955     memcpy(pbBuff, *str, *pcb);
4956     *str += *pcb;
4957   }
4958   return 0;
4959 }
4960
4961 struct StringWithLength {
4962     int length;
4963     char *buffer;
4964 };
4965
4966 /* This callback is used to handled the null characters in a string. */
4967 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4968                                                    LPBYTE pbBuff,
4969                                                    LONG cb,
4970                                                    LONG *pcb)
4971 {
4972     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4973     int size = str->length;
4974     *pcb = cb;
4975     if (*pcb > size) {
4976       *pcb = size;
4977     }
4978     if (*pcb > 0) {
4979       memcpy(pbBuff, str->buffer, *pcb);
4980       str->buffer += *pcb;
4981       str->length -= *pcb;
4982     }
4983     return 0;
4984 }
4985
4986 static void test_EM_STREAMIN(void)
4987 {
4988   HWND hwndRichEdit = new_richedit(NULL);
4989   LRESULT result;
4990   EDITSTREAM es;
4991   char buffer[1024] = {0};
4992
4993   const char * streamText0 = "{\\rtf1 TestSomeText}";
4994   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4995   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4996
4997   const char * streamText1 =
4998   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4999   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5000   "}\r\n";
5001
5002   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5003   const char * streamText2 =
5004     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5005     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5006     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5007     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5008     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5009     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5010     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5011
5012   const char * streamText3 = "RichEdit1";
5013
5014   const char * streamText4 =
5015       "This text just needs to be long enough to cause run to be split onto "
5016       "two separate lines and make sure the null terminating character is "
5017       "handled properly.\0";
5018   int length4 = strlen(streamText4) + 1;
5019   struct StringWithLength cookieForStream4 = {
5020       length4,
5021       (char *)streamText4,
5022   };
5023
5024   const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5025   int length5 = sizeof(streamText5) / sizeof(WCHAR);
5026   struct StringWithLength cookieForStream5 = {
5027       sizeof(streamText5),
5028       (char *)streamText5,
5029   };
5030
5031   /* Minimal test without \par at the end */
5032   es.dwCookie = (DWORD_PTR)&streamText0;
5033   es.dwError = 0;
5034   es.pfnCallback = test_EM_STREAMIN_esCallback;
5035   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5036   ok(result == 12, "got %ld, expected %d\n", result, 12);
5037
5038   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5039   ok (result  == 12,
5040       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5041   result = strcmp (buffer,"TestSomeText");
5042   ok (result  == 0,
5043       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5044   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5045
5046   /* Native richedit 2.0 ignores last \par */
5047   es.dwCookie = (DWORD_PTR)&streamText0a;
5048   es.dwError = 0;
5049   es.pfnCallback = test_EM_STREAMIN_esCallback;
5050   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5051   ok(result == 12, "got %ld, expected %d\n", result, 12);
5052
5053   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5054   ok (result  == 12,
5055       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5056   result = strcmp (buffer,"TestSomeText");
5057   ok (result  == 0,
5058       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5059   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5060
5061   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5062   es.dwCookie = (DWORD_PTR)&streamText0b;
5063   es.dwError = 0;
5064   es.pfnCallback = test_EM_STREAMIN_esCallback;
5065   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5066   ok(result == 13, "got %ld, expected %d\n", result, 13);
5067
5068   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5069   ok (result  == 14,
5070       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5071   result = strcmp (buffer,"TestSomeText\r\n");
5072   ok (result  == 0,
5073       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5074   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5075
5076   es.dwCookie = (DWORD_PTR)&streamText1;
5077   es.dwError = 0;
5078   es.pfnCallback = test_EM_STREAMIN_esCallback;
5079   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5080   ok(result == 12, "got %ld, expected %d\n", result, 12);
5081
5082   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5083   ok (result  == 12,
5084       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5085   result = strcmp (buffer,"TestSomeText");
5086   ok (result  == 0,
5087       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5088   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5089
5090   es.dwCookie = (DWORD_PTR)&streamText2;
5091   es.dwError = 0;
5092   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5093   ok(result == 0, "got %ld, expected %d\n", result, 0);
5094
5095   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5096   ok (result  == 0,
5097       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5098   ok (strlen(buffer)  == 0,
5099       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5100   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5101
5102   es.dwCookie = (DWORD_PTR)&streamText3;
5103   es.dwError = 0;
5104   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5105   ok(result == 0, "got %ld, expected %d\n", result, 0);
5106
5107   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5108   ok (result  == 0,
5109       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5110   ok (strlen(buffer)  == 0,
5111       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5112   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5113
5114   es.dwCookie = (DWORD_PTR)&cookieForStream4;
5115   es.dwError = 0;
5116   es.pfnCallback = test_EM_STREAMIN_esCallback2;
5117   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5118   ok(result == length4, "got %ld, expected %d\n", result, length4);
5119
5120   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5121   ok (result  == length4,
5122       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5123   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5124
5125   es.dwCookie = (DWORD_PTR)&cookieForStream5;
5126   es.dwError = 0;
5127   es.pfnCallback = test_EM_STREAMIN_esCallback2;
5128   result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5129   ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5130
5131   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5132   ok (result  == length5,
5133       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length5);
5134   ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5135
5136   DestroyWindow(hwndRichEdit);
5137 }
5138
5139 static void test_EM_StreamIn_Undo(void)
5140 {
5141   /* The purpose of this test is to determine when a EM_StreamIn should be
5142    * undoable. This is important because WM_PASTE currently uses StreamIn and
5143    * pasting should always be undoable but streaming isn't always.
5144    *
5145    * cases to test:
5146    * StreamIn plain text without SFF_SELECTION.
5147    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5148    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5149    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5150    * Feel free to add tests for other text modes or StreamIn things.
5151    */
5152
5153
5154   HWND hwndRichEdit = new_richedit(NULL);
5155   LRESULT result;
5156   EDITSTREAM es;
5157   char buffer[1024] = {0};
5158   const char randomtext[] = "Some text";
5159
5160   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5161
5162   /* StreamIn, no SFF_SELECTION */
5163   es.dwCookie = nCallbackCount;
5164   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5165   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5166   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5167   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5168   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5169   result = strcmp (buffer,"test");
5170   ok (result  == 0,
5171       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5172
5173   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5174   ok (result == FALSE,
5175       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5176
5177   /* StreamIn, SFF_SELECTION, but nothing selected */
5178   es.dwCookie = nCallbackCount;
5179   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5180   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5181   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5182   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5183   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5184   result = strcmp (buffer,"testSome text");
5185   ok (result  == 0,
5186       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5187
5188   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5189   ok (result == TRUE,
5190      "EM_STREAMIN with SFF_SELECTION but no selection set "
5191       "should create an undo\n");
5192
5193   /* StreamIn, SFF_SELECTION, with a selection */
5194   es.dwCookie = nCallbackCount;
5195   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5196   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5197   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5198   SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5199   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5200   result = strcmp (buffer,"Sometesttext");
5201   ok (result  == 0,
5202       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5203
5204   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5205   ok (result == TRUE,
5206       "EM_STREAMIN with SFF_SELECTION and selection set "
5207       "should create an undo\n");
5208
5209   DestroyWindow(hwndRichEdit);
5210 }
5211
5212 static BOOL is_em_settextex_supported(HWND hwnd)
5213 {
5214     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5215     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5216 }
5217
5218 static void test_unicode_conversions(void)
5219 {
5220     static const WCHAR tW[] = {'t',0};
5221     static const WCHAR teW[] = {'t','e',0};
5222     static const WCHAR textW[] = {'t','e','s','t',0};
5223     static const char textA[] = "test";
5224     char bufA[64];
5225     WCHAR bufW[64];
5226     HWND hwnd;
5227     int em_settextex_supported, ret;
5228
5229 #define set_textA(hwnd, wm_set_text, txt) \
5230     do { \
5231         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5232         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5233         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5234         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5235         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5236     } while(0)
5237 #define expect_textA(hwnd, wm_get_text, txt) \
5238     do { \
5239         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5240         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5241         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5242         memset(bufA, 0xAA, sizeof(bufA)); \
5243         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5244         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5245         ret = lstrcmpA(bufA, txt); \
5246         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5247     } while(0)
5248
5249 #define set_textW(hwnd, wm_set_text, txt) \
5250     do { \
5251         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5252         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5253         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5254         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5255         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5256     } while(0)
5257 #define expect_textW(hwnd, wm_get_text, txt) \
5258     do { \
5259         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5260         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5261         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5262         memset(bufW, 0xAA, sizeof(bufW)); \
5263         ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5264         ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5265         ret = lstrcmpW(bufW, txt); \
5266         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5267     } while(0)
5268 #define expect_empty(hwnd, wm_get_text) \
5269     do { \
5270         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5271         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5272         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5273         memset(bufA, 0xAA, sizeof(bufA)); \
5274         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5275         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5276         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5277     } while(0)
5278
5279     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5280                            0, 0, 200, 60, 0, 0, 0, 0);
5281     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5282
5283     ret = IsWindowUnicode(hwnd);
5284     ok(ret, "RichEdit20W should be unicode under NT\n");
5285
5286     /* EM_SETTEXTEX is supported starting from version 3.0 */
5287     em_settextex_supported = is_em_settextex_supported(hwnd);
5288     trace("EM_SETTEXTEX is %ssupported on this platform\n",
5289           em_settextex_supported ? "" : "NOT ");
5290
5291     expect_empty(hwnd, WM_GETTEXT);
5292     expect_empty(hwnd, EM_GETTEXTEX);
5293
5294     ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5295     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5296     expect_textA(hwnd, WM_GETTEXT, "t");
5297     expect_textA(hwnd, EM_GETTEXTEX, "t");
5298     expect_textW(hwnd, EM_GETTEXTEX, tW);
5299
5300     ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5301     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5302     expect_textA(hwnd, WM_GETTEXT, "te");
5303     expect_textA(hwnd, EM_GETTEXTEX, "te");
5304     expect_textW(hwnd, EM_GETTEXTEX, teW);
5305
5306     set_textA(hwnd, WM_SETTEXT, NULL);
5307     expect_empty(hwnd, WM_GETTEXT);
5308     expect_empty(hwnd, EM_GETTEXTEX);
5309
5310     set_textA(hwnd, WM_SETTEXT, textA);
5311     expect_textA(hwnd, WM_GETTEXT, textA);
5312     expect_textA(hwnd, EM_GETTEXTEX, textA);
5313     expect_textW(hwnd, EM_GETTEXTEX, textW);
5314
5315     if (em_settextex_supported)
5316     {
5317         set_textA(hwnd, EM_SETTEXTEX, textA);
5318         expect_textA(hwnd, WM_GETTEXT, textA);
5319         expect_textA(hwnd, EM_GETTEXTEX, textA);
5320         expect_textW(hwnd, EM_GETTEXTEX, textW);
5321     }
5322
5323     set_textW(hwnd, WM_SETTEXT, textW);
5324     expect_textW(hwnd, WM_GETTEXT, textW);
5325     expect_textA(hwnd, WM_GETTEXT, textA);
5326     expect_textW(hwnd, EM_GETTEXTEX, textW);
5327     expect_textA(hwnd, EM_GETTEXTEX, textA);
5328
5329     if (em_settextex_supported)
5330     {
5331         set_textW(hwnd, EM_SETTEXTEX, textW);
5332         expect_textW(hwnd, WM_GETTEXT, textW);
5333         expect_textA(hwnd, WM_GETTEXT, textA);
5334         expect_textW(hwnd, EM_GETTEXTEX, textW);
5335         expect_textA(hwnd, EM_GETTEXTEX, textA);
5336     }
5337     DestroyWindow(hwnd);
5338
5339     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5340                            0, 0, 200, 60, 0, 0, 0, 0);
5341     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5342
5343     ret = IsWindowUnicode(hwnd);
5344     ok(!ret, "RichEdit20A should NOT be unicode\n");
5345
5346     set_textA(hwnd, WM_SETTEXT, textA);
5347     expect_textA(hwnd, WM_GETTEXT, textA);
5348     expect_textA(hwnd, EM_GETTEXTEX, textA);
5349     expect_textW(hwnd, EM_GETTEXTEX, textW);
5350
5351     if (em_settextex_supported)
5352     {
5353         set_textA(hwnd, EM_SETTEXTEX, textA);
5354         expect_textA(hwnd, WM_GETTEXT, textA);
5355         expect_textA(hwnd, EM_GETTEXTEX, textA);
5356         expect_textW(hwnd, EM_GETTEXTEX, textW);
5357     }
5358
5359         set_textW(hwnd, WM_SETTEXT, textW);
5360         expect_textW(hwnd, WM_GETTEXT, textW);
5361         expect_textA(hwnd, WM_GETTEXT, textA);
5362         expect_textW(hwnd, EM_GETTEXTEX, textW);
5363         expect_textA(hwnd, EM_GETTEXTEX, textA);
5364
5365     if (em_settextex_supported)
5366     {
5367         set_textW(hwnd, EM_SETTEXTEX, textW);
5368         expect_textW(hwnd, WM_GETTEXT, textW);
5369         expect_textA(hwnd, WM_GETTEXT, textA);
5370         expect_textW(hwnd, EM_GETTEXTEX, textW);
5371         expect_textA(hwnd, EM_GETTEXTEX, textA);
5372     }
5373     DestroyWindow(hwnd);
5374 }
5375
5376 static void test_WM_CHAR(void)
5377 {
5378     HWND hwnd;
5379     int ret;
5380     const char * char_list = "abc\rabc\r";
5381     const char * expected_content_single = "abcabc";
5382     const char * expected_content_multi = "abc\r\nabc\r\n";
5383     char buffer[64] = {0};
5384     const char * p;
5385
5386     /* single-line control must IGNORE carriage returns */
5387     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5388                            0, 0, 200, 60, 0, 0, 0, 0);
5389     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5390
5391     p = char_list;
5392     while (*p != '\0') {
5393         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5394         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5395         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5396         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5397         p++;
5398     }
5399
5400     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5401     ret = strcmp(buffer, expected_content_single);
5402     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5403
5404     DestroyWindow(hwnd);
5405
5406     /* multi-line control inserts CR normally */
5407     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5408                            0, 0, 200, 60, 0, 0, 0, 0);
5409     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5410
5411     p = char_list;
5412     while (*p != '\0') {
5413         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5414         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5415         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5416         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5417         p++;
5418     }
5419
5420     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5421     ret = strcmp(buffer, expected_content_multi);
5422     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5423
5424     DestroyWindow(hwnd);
5425 }
5426
5427 static void test_EM_GETTEXTLENGTHEX(void)
5428 {
5429     HWND hwnd;
5430     GETTEXTLENGTHEX gtl;
5431     int ret;
5432     const char * base_string = "base string";
5433     const char * test_string = "a\nb\n\n\r\n";
5434     const char * test_string_after = "a";
5435     const char * test_string_2 = "a\rtest\rstring";
5436     char buffer[64] = {0};
5437
5438     /* single line */
5439     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5440                            0, 0, 200, 60, 0, 0, 0, 0);
5441     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5442
5443     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5444     gtl.codepage = CP_ACP;
5445     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5446     ok(ret == 0, "ret %d\n",ret);
5447
5448     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5449     gtl.codepage = CP_ACP;
5450     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5451     ok(ret == 0, "ret %d\n",ret);
5452
5453     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5454
5455     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5456     gtl.codepage = CP_ACP;
5457     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5458     ok(ret == strlen(base_string), "ret %d\n",ret);
5459
5460     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5461     gtl.codepage = CP_ACP;
5462     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5463     ok(ret == strlen(base_string), "ret %d\n",ret);
5464
5465     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5466
5467     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5468     gtl.codepage = CP_ACP;
5469     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5470     ok(ret == 1, "ret %d\n",ret);
5471
5472     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5473     gtl.codepage = CP_ACP;
5474     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5475     ok(ret == 1, "ret %d\n",ret);
5476
5477     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5478     ret = strcmp(buffer, test_string_after);
5479     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5480
5481     DestroyWindow(hwnd);
5482
5483     /* multi line */
5484     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5485                            0, 0, 200, 60, 0, 0, 0, 0);
5486     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5487
5488     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5489     gtl.codepage = CP_ACP;
5490     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5491     ok(ret == 0, "ret %d\n",ret);
5492
5493     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5494     gtl.codepage = CP_ACP;
5495     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5496     ok(ret == 0, "ret %d\n",ret);
5497
5498     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5499
5500     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5501     gtl.codepage = CP_ACP;
5502     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5503     ok(ret == strlen(base_string), "ret %d\n",ret);
5504
5505     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5506     gtl.codepage = CP_ACP;
5507     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5508     ok(ret == strlen(base_string), "ret %d\n",ret);
5509
5510     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5511
5512     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5513     gtl.codepage = CP_ACP;
5514     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5515     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5516
5517     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5518     gtl.codepage = CP_ACP;
5519     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5520     ok(ret == strlen(test_string_2), "ret %d\n",ret);
5521
5522     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5523
5524     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5525     gtl.codepage = CP_ACP;
5526     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5527     ok(ret == 10, "ret %d\n",ret);
5528
5529     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5530     gtl.codepage = CP_ACP;
5531     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5532     ok(ret == 6, "ret %d\n",ret);
5533
5534     /* Unicode/NUMCHARS/NUMBYTES */
5535     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5536
5537     gtl.flags = GTL_DEFAULT;
5538     gtl.codepage = 1200;
5539     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5540     ok(ret == lstrlen(test_string_2),
5541        "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5542
5543     gtl.flags = GTL_NUMCHARS;
5544     gtl.codepage = 1200;
5545     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5546     ok(ret == lstrlen(test_string_2),
5547        "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5548
5549     gtl.flags = GTL_NUMBYTES;
5550     gtl.codepage = 1200;
5551     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5552     ok(ret == lstrlen(test_string_2)*2,
5553        "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5554
5555     gtl.flags = GTL_PRECISE;
5556     gtl.codepage = 1200;
5557     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5558     ok(ret == lstrlen(test_string_2)*2,
5559        "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5560
5561     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5562     gtl.codepage = 1200;
5563     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5564     ok(ret == lstrlen(test_string_2),
5565        "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5566
5567     gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5568     gtl.codepage = 1200;
5569     ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5570     ok(ret == E_INVALIDARG,
5571        "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5572
5573     DestroyWindow(hwnd);
5574 }
5575
5576
5577 /* globals that parent and child access when checking event masks & notifications */
5578 static HWND eventMaskEditHwnd = 0;
5579 static int queriedEventMask;
5580 static int watchForEventMask = 0;
5581
5582 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5583 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5584 {
5585     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5586     {
5587       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5588     }
5589     return DefWindowProcA(hwnd, message, wParam, lParam);
5590 }
5591
5592 /* test event masks in combination with WM_COMMAND */
5593 static void test_eventMask(void)
5594 {
5595     HWND parent;
5596     int ret, style;
5597     WNDCLASSA cls;
5598     const char text[] = "foo bar\n";
5599     int eventMask;
5600
5601     /* register class to capture WM_COMMAND */
5602     cls.style = 0;
5603     cls.lpfnWndProc = ParentMsgCheckProcA;
5604     cls.cbClsExtra = 0;
5605     cls.cbWndExtra = 0;
5606     cls.hInstance = GetModuleHandleA(0);
5607     cls.hIcon = 0;
5608     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5609     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5610     cls.lpszMenuName = NULL;
5611     cls.lpszClassName = "EventMaskParentClass";
5612     if(!RegisterClassA(&cls)) assert(0);
5613
5614     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5615                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5616     ok (parent != 0, "Failed to create parent window\n");
5617
5618     eventMaskEditHwnd = new_richedit(parent);
5619     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5620
5621     eventMask = ENM_CHANGE | ENM_UPDATE;
5622     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5623     ok(ret == ENM_NONE, "wrong event mask\n");
5624     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5625     ok(ret == eventMask, "failed to set event mask\n");
5626
5627     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5628     queriedEventMask = 0;  /* initialize to something other than we expect */
5629     watchForEventMask = EN_CHANGE;
5630     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5631     ok(ret == TRUE, "failed to set text\n");
5632     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5633        notification in response to WM_SETTEXT */
5634     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5635             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5636
5637     /* check to see if EN_CHANGE is sent when redraw is turned off */
5638     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5639     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5640     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5641     /* redraw is disabled by making the window invisible. */
5642     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5643     queriedEventMask = 0;  /* initialize to something other than we expect */
5644     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5645     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5646             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5647     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5648     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5649
5650     /* check to see if EN_UPDATE is sent when the editor isn't visible */
5651     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5652     style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5653     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5654     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5655     watchForEventMask = EN_UPDATE;
5656     queriedEventMask = 0;  /* initialize to something other than we expect */
5657     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5658     ok(queriedEventMask == 0,
5659             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5660     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5661     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5662     queriedEventMask = 0;  /* initialize to something other than we expect */
5663     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5664     ok(queriedEventMask == eventMask,
5665             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5666
5667
5668     DestroyWindow(parent);
5669 }
5670
5671 static int received_WM_NOTIFY = 0;
5672 static int modify_at_WM_NOTIFY = 0;
5673 static BOOL filter_on_WM_NOTIFY = FALSE;
5674 static HWND hwndRichedit_WM_NOTIFY;
5675
5676 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5677 {
5678     if(message == WM_NOTIFY)
5679     {
5680       received_WM_NOTIFY = 1;
5681       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5682       if (filter_on_WM_NOTIFY) return TRUE;
5683     }
5684     return DefWindowProcA(hwnd, message, wParam, lParam);
5685 }
5686
5687 static void test_WM_NOTIFY(void)
5688 {
5689     HWND parent;
5690     WNDCLASSA cls;
5691     CHARFORMAT2 cf2;
5692     int sel_start, sel_end;
5693
5694     /* register class to capture WM_NOTIFY */
5695     cls.style = 0;
5696     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5697     cls.cbClsExtra = 0;
5698     cls.cbWndExtra = 0;
5699     cls.hInstance = GetModuleHandleA(0);
5700     cls.hIcon = 0;
5701     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5702     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5703     cls.lpszMenuName = NULL;
5704     cls.lpszClassName = "WM_NOTIFY_ParentClass";
5705     if(!RegisterClassA(&cls)) assert(0);
5706
5707     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5708                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5709     ok (parent != 0, "Failed to create parent window\n");
5710
5711     hwndRichedit_WM_NOTIFY = new_richedit(parent);
5712     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5713
5714     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5715
5716     /* Notifications for selection change should only be sent when selection
5717        actually changes. EM_SETCHARFORMAT is one message that calls
5718        ME_CommitUndo, which should check whether message should be sent */
5719     received_WM_NOTIFY = 0;
5720     cf2.cbSize = sizeof(CHARFORMAT2);
5721     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5722     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5723     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5724     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5725     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5726
5727     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5728        already at 0. */
5729     received_WM_NOTIFY = 0;
5730     modify_at_WM_NOTIFY = 0;
5731     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5732     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5733     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5734
5735     received_WM_NOTIFY = 0;
5736     modify_at_WM_NOTIFY = 0;
5737     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5738     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5739
5740     received_WM_NOTIFY = 0;
5741     modify_at_WM_NOTIFY = 0;
5742     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5743     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5744     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5745
5746     /* Test for WM_NOTIFY messages with redraw disabled. */
5747     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5748     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5749     received_WM_NOTIFY = 0;
5750     SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5751     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5752     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5753
5754     /* Test filtering key events. */
5755     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5756     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5757     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5758     received_WM_NOTIFY = 0;
5759     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5760     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5761     ok(sel_start == 1 && sel_end == 1,
5762        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5763     filter_on_WM_NOTIFY = TRUE;
5764     received_WM_NOTIFY = 0;
5765     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5766     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5767     ok(sel_start == 1 && sel_end == 1,
5768        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5769
5770     /* test with owner set to NULL */
5771     SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5772     SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5773     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5774     ok(sel_start == 1 && sel_end == 1,
5775        "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5776
5777     DestroyWindow(hwndRichedit_WM_NOTIFY);
5778     DestroyWindow(parent);
5779 }
5780
5781 static void test_undo_coalescing(void)
5782 {
5783     HWND hwnd;
5784     int result;
5785     char buffer[64] = {0};
5786
5787     /* multi-line control inserts CR normally */
5788     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5789                            0, 0, 200, 60, 0, 0, 0, 0);
5790     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5791
5792     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5793     ok (result == FALSE, "Can undo after window creation.\n");
5794     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5795     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5796     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5797     ok (result == FALSE, "Can redo after window creation.\n");
5798     result = SendMessage(hwnd, EM_REDO, 0, 0);
5799     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5800
5801     /* Test the effect of arrows keys during typing on undo transactions*/
5802     simulate_typing_characters(hwnd, "one two three");
5803     SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5804     SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5805     simulate_typing_characters(hwnd, " four five six");
5806
5807     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5808     ok (result == FALSE, "Can redo before anything is undone.\n");
5809     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5810     ok (result == TRUE, "Cannot undo typed characters.\n");
5811     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5812     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5813     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5814     ok (result == TRUE, "Cannot redo after undo.\n");
5815     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5816     result = strcmp(buffer, "one two three");
5817     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5818
5819     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5820     ok (result == TRUE, "Cannot undo typed characters.\n");
5821     result = SendMessage(hwnd, WM_UNDO, 0, 0);
5822     ok (result == TRUE, "Failed to undo typed characters.\n");
5823     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5824     result = strcmp(buffer, "");
5825     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5826
5827     /* Test the effect of focus changes during typing on undo transactions*/
5828     simulate_typing_characters(hwnd, "one two three");
5829     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5830     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5831     SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5832     SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5833     simulate_typing_characters(hwnd, " four five six");
5834     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5835     ok (result == TRUE, "Failed to undo typed characters.\n");
5836     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5837     result = strcmp(buffer, "one two three");
5838     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5839
5840     /* Test the effect of the back key during typing on undo transactions */
5841     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5842     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5843     ok (result == TRUE, "Failed to clear the text.\n");
5844     simulate_typing_characters(hwnd, "one two threa");
5845     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5846     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5847     SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5848     SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5849     simulate_typing_characters(hwnd, "e four five six");
5850     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5851     ok (result == TRUE, "Failed to undo typed characters.\n");
5852     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5853     result = strcmp(buffer, "");
5854     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5855
5856     /* Test the effect of the delete key during typing on undo transactions */
5857     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5858     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5859     ok(result == TRUE, "Failed to set the text.\n");
5860     SendMessage(hwnd, EM_SETSEL, 1, 1);
5861     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5862     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5863     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5864     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5865     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5866     ok (result == TRUE, "Failed to undo typed characters.\n");
5867     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5868     result = strcmp(buffer, "acd");
5869     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5870     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5871     ok (result == TRUE, "Failed to undo typed characters.\n");
5872     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5873     result = strcmp(buffer, "abcd");
5874     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5875
5876     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5877     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5878     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5879     ok (result == TRUE, "Failed to clear the text.\n");
5880     simulate_typing_characters(hwnd, "one two three");
5881     result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5882     ok (result == 0, "expected %d but got %d\n", 0, result);
5883     simulate_typing_characters(hwnd, " four five six");
5884     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5885     ok (result == TRUE, "Failed to undo typed characters.\n");
5886     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5887     result = strcmp(buffer, "one two three");
5888     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5889     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5890     ok (result == TRUE, "Failed to undo typed characters.\n");
5891     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5892     result = strcmp(buffer, "");
5893     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5894
5895     DestroyWindow(hwnd);
5896 }
5897
5898 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5899 {
5900     int length;
5901
5902     /* MSDN lied, length is actually the number of bytes. */
5903     length = bytes / sizeof(WCHAR);
5904     switch(code)
5905     {
5906         case WB_ISDELIMITER:
5907             return text[pos] == 'X';
5908         case WB_LEFT:
5909         case WB_MOVEWORDLEFT:
5910             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5911                 return pos-1;
5912             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5913         case WB_LEFTBREAK:
5914             pos--;
5915             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5916                 pos--;
5917             return pos;
5918         case WB_RIGHT:
5919         case WB_MOVEWORDRIGHT:
5920             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5921                 return pos+1;
5922             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5923         case WB_RIGHTBREAK:
5924             pos++;
5925             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5926                 pos++;
5927             return pos;
5928         default:
5929             ok(FALSE, "Unexpected code %d\n", code);
5930             break;
5931     }
5932     return 0;
5933 }
5934
5935 static void test_word_movement(void)
5936 {
5937     HWND hwnd;
5938     int result;
5939     int sel_start, sel_end;
5940     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5941
5942     /* multi-line control inserts CR normally */
5943     hwnd = new_richedit(NULL);
5944
5945     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
5946     ok (result == TRUE, "Failed to clear the text.\n");
5947     SendMessage(hwnd, EM_SETSEL, 0, 0);
5948     /* |one two three */
5949
5950     send_ctrl_key(hwnd, VK_RIGHT);
5951     /* one |two  three */
5952     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5953     ok(sel_start == sel_end, "Selection should be empty\n");
5954     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5955
5956     send_ctrl_key(hwnd, VK_RIGHT);
5957     /* one two  |three */
5958     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5959     ok(sel_start == sel_end, "Selection should be empty\n");
5960     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5961
5962     send_ctrl_key(hwnd, VK_LEFT);
5963     /* one |two  three */
5964     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5965     ok(sel_start == sel_end, "Selection should be empty\n");
5966     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5967
5968     send_ctrl_key(hwnd, VK_LEFT);
5969     /* |one two  three */
5970     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5971     ok(sel_start == sel_end, "Selection should be empty\n");
5972     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5973
5974     SendMessage(hwnd, EM_SETSEL, 8, 8);
5975     /* one two | three */
5976     send_ctrl_key(hwnd, VK_RIGHT);
5977     /* one two  |three */
5978     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5979     ok(sel_start == sel_end, "Selection should be empty\n");
5980     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5981
5982     SendMessage(hwnd, EM_SETSEL, 11, 11);
5983     /* one two  th|ree */
5984     send_ctrl_key(hwnd, VK_LEFT);
5985     /* one two  |three */
5986     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5987     ok(sel_start == sel_end, "Selection should be empty\n");
5988     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5989
5990     /* Test with a custom word break procedure that uses X as the delimiter. */
5991     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5992     ok (result == TRUE, "Failed to clear the text.\n");
5993     SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5994     /* |one twoXthree */
5995     send_ctrl_key(hwnd, VK_RIGHT);
5996     /* one twoX|three */
5997     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5998     ok(sel_start == sel_end, "Selection should be empty\n");
5999     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6000
6001     DestroyWindow(hwnd);
6002
6003     /* Make sure the behaviour is the same with a unicode richedit window,
6004      * and using unicode functions. */
6005
6006     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6007                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6008                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6009
6010     /* Test with a custom word break procedure that uses X as the delimiter. */
6011     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6012     ok (result == TRUE, "Failed to clear the text.\n");
6013     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6014     /* |one twoXthree */
6015     send_ctrl_key(hwnd, VK_RIGHT);
6016     /* one twoX|three */
6017     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6018     ok(sel_start == sel_end, "Selection should be empty\n");
6019     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6020
6021     DestroyWindow(hwnd);
6022 }
6023
6024 static void test_EM_CHARFROMPOS(void)
6025 {
6026     HWND hwnd;
6027     int result;
6028     RECT rcClient;
6029     POINTL point;
6030     point.x = 0;
6031     point.y = 40;
6032
6033     /* multi-line control inserts CR normally */
6034     hwnd = new_richedit(NULL);
6035     result = SendMessageA(hwnd, WM_SETTEXT, 0,
6036                           (LPARAM)"one two three four five six seven\reight");
6037     ok(result == 1, "Expected 1, got %d\n", result);
6038     GetClientRect(hwnd, &rcClient);
6039
6040     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6041     ok(result == 34, "expected character index of 34 but got %d\n", result);
6042
6043     /* Test with points outside the bounds of the richedit control. */
6044     point.x = -1;
6045     point.y = 40;
6046     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6047     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6048
6049     point.x = 1000;
6050     point.y = 0;
6051     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6052     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6053
6054     point.x = 1000;
6055     point.y = 36;
6056     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6057     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6058
6059     point.x = 1000;
6060     point.y = -1;
6061     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6062     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6063
6064     point.x = 1000;
6065     point.y = rcClient.bottom + 1;
6066     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6067     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6068
6069     point.x = 1000;
6070     point.y = rcClient.bottom;
6071     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6072     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6073
6074     DestroyWindow(hwnd);
6075 }
6076
6077 static void test_word_wrap(void)
6078 {
6079     HWND hwnd;
6080     POINTL point = {0, 60}; /* This point must be below the first line */
6081     const char *text = "Must be long enough to test line wrapping";
6082     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6083     int res, pos, lines;
6084
6085     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6086      * when specified on window creation and set later. */
6087     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6088                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6089     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6090     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6091     ok(res, "WM_SETTEXT failed.\n");
6092     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6093     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6094     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6095     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6096
6097     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6098     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6099     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6100     DestroyWindow(hwnd);
6101
6102     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6103                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6104     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6105
6106     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6107     ok(res, "WM_SETTEXT failed.\n");
6108     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6109     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6110     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6111     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6112
6113     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6114     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6115     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6116     DestroyWindow(hwnd);
6117
6118     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6119                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6120     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6121     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6122     ok(res, "WM_SETTEXT failed.\n");
6123     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6124     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6125
6126     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6127     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6128     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6129     DestroyWindow(hwnd);
6130
6131     hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6132                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6133                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6134     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6135     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6136     ok(res, "WM_SETTEXT failed.\n");
6137     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6138     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6139
6140     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6141     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6142     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6143
6144     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6145     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6146     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6147     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6148     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6149
6150     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6151     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6152     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6153     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6154     DestroyWindow(hwnd);
6155
6156     /* Test to see if wrapping happens with redraw disabled. */
6157     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6158                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6159     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6160     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6161     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6162     ok(res, "EM_REPLACESEL failed.\n");
6163     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6164     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6165     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6166     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6167     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6168
6169     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6170     DestroyWindow(hwnd);
6171 }
6172
6173 static void test_autoscroll(void)
6174 {
6175     HWND hwnd = new_richedit(NULL);
6176     int lines, ret, redraw;
6177     POINT pt;
6178
6179     for (redraw = 0; redraw <= 1; redraw++) {
6180         trace("testing with WM_SETREDRAW=%d\n", redraw);
6181         SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6182         SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6183         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6184         ok(lines == 8, "%d lines instead of 8\n", lines);
6185         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6186         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6187         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6188         ret = GetWindowLong(hwnd, GWL_STYLE);
6189         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6190
6191         SendMessage(hwnd, WM_SETTEXT, 0, 0);
6192         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6193         ok(lines == 1, "%d lines instead of 1\n", lines);
6194         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6195         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6196         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6197         ret = GetWindowLong(hwnd, GWL_STYLE);
6198         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6199     }
6200
6201     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6202     DestroyWindow(hwnd);
6203
6204     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6205      * auto vertical/horizontal scrolling options. */
6206     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6207                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6208                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6209     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6210     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6211     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6212     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6213     ret = GetWindowLong(hwnd, GWL_STYLE);
6214     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6215     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6216     DestroyWindow(hwnd);
6217
6218     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6219                           WS_POPUP|ES_MULTILINE,
6220                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6221     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6222     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6223     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6224     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6225     ret = GetWindowLong(hwnd, GWL_STYLE);
6226     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6227     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6228     DestroyWindow(hwnd);
6229 }
6230
6231
6232 static void test_format_rect(void)
6233 {
6234     HWND hwnd;
6235     RECT rc, expected, clientRect;
6236     int n;
6237     DWORD options;
6238
6239     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6240                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6241                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6242     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6243
6244     GetClientRect(hwnd, &clientRect);
6245
6246     expected = clientRect;
6247     expected.left += 1;
6248     expected.right -= 1;
6249     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6250     ok(rc.top == expected.top && rc.left == expected.left &&
6251        rc.bottom == expected.bottom && rc.right == expected.right,
6252        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6253        rc.top, rc.left, rc.bottom, rc.right,
6254        expected.top, expected.left, expected.bottom, expected.right);
6255
6256     for (n = -3; n <= 3; n++)
6257     {
6258       rc = clientRect;
6259       rc.top += n;
6260       rc.left += n;
6261       rc.bottom -= n;
6262       rc.right -= n;
6263       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6264
6265       expected = rc;
6266       expected.top = max(0, rc.top);
6267       expected.left = max(0, rc.left);
6268       expected.bottom = min(clientRect.bottom, rc.bottom);
6269       expected.right = min(clientRect.right, rc.right);
6270       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6271       ok(rc.top == expected.top && rc.left == expected.left &&
6272          rc.bottom == expected.bottom && rc.right == expected.right,
6273          "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6274          n, rc.top, rc.left, rc.bottom, rc.right,
6275          expected.top, expected.left, expected.bottom, expected.right);
6276     }
6277
6278     rc = clientRect;
6279     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6280     expected = clientRect;
6281     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6282     ok(rc.top == expected.top && rc.left == expected.left &&
6283        rc.bottom == expected.bottom && rc.right == expected.right,
6284        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6285        rc.top, rc.left, rc.bottom, rc.right,
6286        expected.top, expected.left, expected.bottom, expected.right);
6287
6288     /* Adding the selectionbar adds the selectionbar width to the left side. */
6289     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6290     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6291     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6292     expected.left += 8; /* selection bar width */
6293     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6294     ok(rc.top == expected.top && rc.left == expected.left &&
6295        rc.bottom == expected.bottom && rc.right == expected.right,
6296        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6297        rc.top, rc.left, rc.bottom, rc.right,
6298        expected.top, expected.left, expected.bottom, expected.right);
6299
6300     rc = clientRect;
6301     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6302     expected = clientRect;
6303     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6304     ok(rc.top == expected.top && rc.left == expected.left &&
6305        rc.bottom == expected.bottom && rc.right == expected.right,
6306        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6307        rc.top, rc.left, rc.bottom, rc.right,
6308        expected.top, expected.left, expected.bottom, expected.right);
6309
6310     /* Removing the selectionbar subtracts the selectionbar width from the left side,
6311      * even if the left side is already 0. */
6312     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6313     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6314     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6315     expected.left -= 8; /* selection bar width */
6316     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6317     ok(rc.top == expected.top && rc.left == expected.left &&
6318        rc.bottom == expected.bottom && rc.right == expected.right,
6319        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6320        rc.top, rc.left, rc.bottom, rc.right,
6321        expected.top, expected.left, expected.bottom, expected.right);
6322
6323     /* Set the absolute value of the formatting rectangle. */
6324     rc = clientRect;
6325     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6326     expected = clientRect;
6327     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6328     ok(rc.top == expected.top && rc.left == expected.left &&
6329        rc.bottom == expected.bottom && rc.right == expected.right,
6330        "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6331        n, rc.top, rc.left, rc.bottom, rc.right,
6332        expected.top, expected.left, expected.bottom, expected.right);
6333
6334     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6335      * LPARAM as being a relative offset when the WPARAM value is 1, but these
6336      * tests show that this isn't true. */
6337     rc.top = 15;
6338     rc.left = 15;
6339     rc.bottom = clientRect.bottom - 15;
6340     rc.right = clientRect.right - 15;
6341     expected = rc;
6342     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6343     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6344     ok(rc.top == expected.top && rc.left == expected.left &&
6345        rc.bottom == expected.bottom && rc.right == expected.right,
6346        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6347        rc.top, rc.left, rc.bottom, rc.right,
6348        expected.top, expected.left, expected.bottom, expected.right);
6349
6350     /* For some reason it does not limit the values to the client rect with
6351      * a WPARAM value of 1. */
6352     rc.top = -15;
6353     rc.left = -15;
6354     rc.bottom = clientRect.bottom + 15;
6355     rc.right = clientRect.right + 15;
6356     expected = rc;
6357     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6358     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6359     ok(rc.top == expected.top && rc.left == expected.left &&
6360        rc.bottom == expected.bottom && rc.right == expected.right,
6361        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6362        rc.top, rc.left, rc.bottom, rc.right,
6363        expected.top, expected.left, expected.bottom, expected.right);
6364
6365     /* Reset to default rect and check how the format rect adjusts to window
6366      * resize and how it copes with very small windows */
6367     SendMessageA(hwnd, EM_SETRECT, 0, 0);
6368
6369     MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6370     GetClientRect(hwnd, &clientRect);
6371
6372     expected = clientRect;
6373     expected.left += 1;
6374     expected.right -= 1;
6375     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6376     ok(rc.top == expected.top && rc.left == expected.left &&
6377        rc.bottom == expected.bottom && rc.right == expected.right,
6378        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6379        rc.top, rc.left, rc.bottom, rc.right,
6380        expected.top, expected.left, expected.bottom, expected.right);
6381
6382     MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6383     GetClientRect(hwnd, &clientRect);
6384
6385     expected = clientRect;
6386     expected.left += 1;
6387     expected.right -= 1;
6388     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6389     ok(rc.top == expected.top && rc.left == expected.left &&
6390        rc.bottom == expected.bottom && rc.right == expected.right,
6391        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6392        rc.top, rc.left, rc.bottom, rc.right,
6393        expected.top, expected.left, expected.bottom, expected.right);
6394
6395     MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6396     GetClientRect(hwnd, &clientRect);
6397
6398     expected = clientRect;
6399     expected.left += 1;
6400     expected.right -= 1;
6401     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6402     ok(rc.top == expected.top && rc.left == expected.left &&
6403        rc.bottom == expected.bottom && rc.right == expected.right,
6404        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6405        rc.top, rc.left, rc.bottom, rc.right,
6406        expected.top, expected.left, expected.bottom, expected.right);
6407
6408     DestroyWindow(hwnd);
6409
6410     /* The extended window style affects the formatting rectangle. */
6411     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6412                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6413                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6414     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6415
6416     GetClientRect(hwnd, &clientRect);
6417
6418     expected = clientRect;
6419     expected.left += 1;
6420     expected.top += 1;
6421     expected.right -= 1;
6422     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6423     ok(rc.top == expected.top && rc.left == expected.left &&
6424        rc.bottom == expected.bottom && rc.right == expected.right,
6425        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6426        rc.top, rc.left, rc.bottom, rc.right,
6427        expected.top, expected.left, expected.bottom, expected.right);
6428
6429     rc = clientRect;
6430     rc.top += 5;
6431     rc.left += 5;
6432     rc.bottom -= 5;
6433     rc.right -= 5;
6434     expected = rc;
6435     expected.top -= 1;
6436     expected.left -= 1;
6437     expected.right += 1;
6438     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6439     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6440     ok(rc.top == expected.top && rc.left == expected.left &&
6441        rc.bottom == expected.bottom && rc.right == expected.right,
6442        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6443        rc.top, rc.left, rc.bottom, rc.right,
6444        expected.top, expected.left, expected.bottom, expected.right);
6445
6446     DestroyWindow(hwnd);
6447 }
6448
6449 static void test_WM_GETDLGCODE(void)
6450 {
6451     HWND hwnd;
6452     UINT res, expected;
6453     MSG msg;
6454
6455     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6456
6457     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6458                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6459                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6460     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6461     msg.hwnd = hwnd;
6462     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6463     expected = expected | DLGC_WANTMESSAGE;
6464     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6465        res, expected);
6466     DestroyWindow(hwnd);
6467
6468     msg.message = WM_KEYDOWN;
6469     msg.wParam = VK_RETURN;
6470     msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6471     msg.pt.x = 0;
6472     msg.pt.y = 0;
6473     msg.time = GetTickCount();
6474
6475     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6476                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6477                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6478     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6479     msg.hwnd = hwnd;
6480     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6481     expected = expected | DLGC_WANTMESSAGE;
6482     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6483        res, expected);
6484     DestroyWindow(hwnd);
6485
6486     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6487                           ES_MULTILINE|WS_POPUP,
6488                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6489     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6490     msg.hwnd = hwnd;
6491     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6492     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6493     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6494        res, expected);
6495     DestroyWindow(hwnd);
6496
6497     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6498                           ES_WANTRETURN|WS_POPUP,
6499                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6500     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6501     msg.hwnd = hwnd;
6502     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6503     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6504     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6505        res, expected);
6506     DestroyWindow(hwnd);
6507
6508     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6509                           WS_POPUP,
6510                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6511     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6512     msg.hwnd = hwnd;
6513     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6514     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6515     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6516        res, expected);
6517     DestroyWindow(hwnd);
6518
6519     msg.wParam = VK_TAB;
6520     msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6521
6522     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6523                           ES_MULTILINE|WS_POPUP,
6524                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6525     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6526     msg.hwnd = hwnd;
6527     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6528     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6529     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6530        res, expected);
6531     DestroyWindow(hwnd);
6532
6533     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6534                           WS_POPUP,
6535                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6536     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6537     msg.hwnd = hwnd;
6538     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6539     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6540     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6541        res, expected);
6542     DestroyWindow(hwnd);
6543
6544     hold_key(VK_CONTROL);
6545
6546     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6547                           ES_MULTILINE|WS_POPUP,
6548                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6549     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6550     msg.hwnd = hwnd;
6551     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6552     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6553     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6554        res, expected);
6555     DestroyWindow(hwnd);
6556
6557     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6558                           WS_POPUP,
6559                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6560     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6561     msg.hwnd = hwnd;
6562     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6563     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6564     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6565        res, expected);
6566     DestroyWindow(hwnd);
6567
6568     release_key(VK_CONTROL);
6569
6570     msg.wParam = 'a';
6571     msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6572
6573     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6574                           ES_MULTILINE|WS_POPUP,
6575                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6576     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6577     msg.hwnd = hwnd;
6578     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6579     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6580     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6581        res, expected);
6582     DestroyWindow(hwnd);
6583
6584     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6585                           WS_POPUP,
6586                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6587     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6588     msg.hwnd = hwnd;
6589     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6590     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6591     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6592        res, expected);
6593     DestroyWindow(hwnd);
6594
6595     msg.message = WM_CHAR;
6596
6597     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6598                           ES_MULTILINE|WS_POPUP,
6599                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6600     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6601     msg.hwnd = hwnd;
6602     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6603     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6604     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6605        res, expected);
6606     DestroyWindow(hwnd);
6607
6608     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6609                           WS_POPUP,
6610                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6611     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6612     msg.hwnd = hwnd;
6613     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6614     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6615     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6616        res, expected);
6617     DestroyWindow(hwnd);
6618 }
6619
6620 static void test_zoom(void)
6621 {
6622     HWND hwnd;
6623     UINT ret;
6624     RECT rc;
6625     POINT pt;
6626     int numerator, denominator;
6627
6628     hwnd = new_richedit(NULL);
6629     GetClientRect(hwnd, &rc);
6630     pt.x = (rc.right - rc.left) / 2;
6631     pt.y = (rc.bottom - rc.top) / 2;
6632     ClientToScreen(hwnd, &pt);
6633
6634     /* Test initial zoom value */
6635     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6636     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6637     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6638     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6639
6640     /* test scroll wheel */
6641     hold_key(VK_CONTROL);
6642     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6643                       MAKELPARAM(pt.x, pt.y));
6644     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6645     release_key(VK_CONTROL);
6646
6647     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6648     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6649     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6650     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6651
6652     /* Test how much the mouse wheel can zoom in and out. */
6653     ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6654     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6655
6656     hold_key(VK_CONTROL);
6657     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6658                       MAKELPARAM(pt.x, pt.y));
6659     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6660     release_key(VK_CONTROL);
6661
6662     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6663     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6664     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6665     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6666
6667     ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6668     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6669
6670     hold_key(VK_CONTROL);
6671     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6672                       MAKELPARAM(pt.x, pt.y));
6673     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6674     release_key(VK_CONTROL);
6675
6676     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6677     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6678     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6679     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6680
6681     ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6682     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6683
6684     hold_key(VK_CONTROL);
6685     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6686                       MAKELPARAM(pt.x, pt.y));
6687     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6688     release_key(VK_CONTROL);
6689
6690     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6691     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6692     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6693     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6694
6695     ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6696     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6697
6698     hold_key(VK_CONTROL);
6699     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6700                       MAKELPARAM(pt.x, pt.y));
6701     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6702     release_key(VK_CONTROL);
6703
6704     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6705     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6706     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6707     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6708
6709     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6710     ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6711     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6712
6713     hold_key(VK_CONTROL);
6714     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6715                       MAKELPARAM(pt.x, pt.y));
6716     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6717     release_key(VK_CONTROL);
6718
6719     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6720     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6721     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6722     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6723
6724     /* Test bounds checking on EM_SETZOOM */
6725     ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6726     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6727
6728     ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6729     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6730
6731     ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6732     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6733
6734     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6735     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6736     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6737     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6738
6739     ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6740     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6741
6742     /* See if negative numbers are accepted. */
6743     ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6744     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6745
6746     /* See if negative numbers are accepted. */
6747     ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6748     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6749
6750     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6751     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6752     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6753     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6754
6755     /* Reset the zoom value */
6756     ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6757     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6758
6759     DestroyWindow(hwnd);
6760 }
6761
6762 struct dialog_mode_messages
6763 {
6764     int wm_getdefid, wm_close, wm_nextdlgctl;
6765 };
6766
6767 static struct dialog_mode_messages dm_messages;
6768
6769 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6770     ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6771     "got %d\n", wmclose, dm_messages.wm_close); \
6772     ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6773     "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6774     ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6775     "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6776
6777 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6778 {
6779     switch (iMsg)
6780     {
6781         case DM_GETDEFID:
6782             dm_messages.wm_getdefid++;
6783             return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6784         case WM_NEXTDLGCTL:
6785             dm_messages.wm_nextdlgctl++;
6786             break;
6787         case WM_CLOSE:
6788             dm_messages.wm_close++;
6789             break;
6790     }
6791
6792     return DefWindowProc(hwnd, iMsg, wParam, lParam);
6793 }
6794
6795 static void test_dialogmode(void)
6796 {
6797     HWND hwRichEdit, hwParent, hwButton;
6798     MSG msg= {0};
6799     int lcount, r;
6800     WNDCLASSA cls;
6801
6802     cls.style = 0;
6803     cls.lpfnWndProc = dialog_mode_wnd_proc;
6804     cls.cbClsExtra = 0;
6805     cls.cbWndExtra = 0;
6806     cls.hInstance = GetModuleHandleA(0);
6807     cls.hIcon = 0;
6808     cls.hCursor = LoadCursorA(0, IDC_ARROW);
6809     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6810     cls.lpszMenuName = NULL;
6811     cls.lpszClassName = "DialogModeParentClass";
6812     if(!RegisterClassA(&cls)) assert(0);
6813
6814     hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6815       CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6816
6817     /* Test richedit(ES_MULTILINE) */
6818
6819     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6820
6821     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6822     ok(0 == r, "expected 0, got %d\n", r);
6823     lcount = SendMessage(hwRichEdit,  EM_GETLINECOUNT, 0, 0);
6824     ok(2 == lcount, "expected 2, got %d\n", lcount);
6825
6826     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6827     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6828
6829     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6830     ok(0 == r, "expected 0, got %d\n", r);
6831     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6832     ok(3 == lcount, "expected 3, got %d\n", lcount);
6833
6834     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6835     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6836     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6837     ok(0 == r, "expected 0, got %d\n", r);
6838     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6839     ok(3 == lcount, "expected 3, got %d\n", lcount);
6840
6841     DestroyWindow(hwRichEdit);
6842
6843     /* Test standalone richedit(ES_MULTILINE) */
6844
6845     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6846
6847     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6848     ok(0 == r, "expected 0, got %d\n", r);
6849     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6850     ok(2 == lcount, "expected 2, got %d\n", lcount);
6851
6852     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6853     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6854
6855     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6856     ok(0 == r, "expected 0, got %d\n", r);
6857     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6858     ok(2 == lcount, "expected 2, got %d\n", lcount);
6859
6860     DestroyWindow(hwRichEdit);
6861
6862     /* Check  a destination for messages */
6863
6864     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6865
6866     SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6867     SetParent( hwRichEdit, NULL);
6868
6869     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6870     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6871
6872     memset(&dm_messages, 0, sizeof(dm_messages));
6873     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6874     ok(0 == r, "expected 0, got %d\n", r);
6875     test_dm_messages(0, 1, 0);
6876
6877     memset(&dm_messages, 0, sizeof(dm_messages));
6878     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6879     ok(0 == r, "expected 0, got %d\n", r);
6880     test_dm_messages(0, 0, 1);
6881
6882     DestroyWindow(hwRichEdit);
6883
6884     /* Check messages from richedit(ES_MULTILINE) */
6885
6886     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6887
6888     memset(&dm_messages, 0, sizeof(dm_messages));
6889     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6890     ok(0 == r, "expected 0, got %d\n", r);
6891     test_dm_messages(0, 0, 0);
6892
6893     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6894     ok(2 == lcount, "expected 2, got %d\n", lcount);
6895
6896     memset(&dm_messages, 0, sizeof(dm_messages));
6897     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6898     ok(0 == r, "expected 0, got %d\n", r);
6899     test_dm_messages(0, 0, 0);
6900
6901     memset(&dm_messages, 0, sizeof(dm_messages));
6902     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6903     ok(0 == r, "expected 0, got %d\n", r);
6904     test_dm_messages(0, 0, 0);
6905
6906     memset(&dm_messages, 0, sizeof(dm_messages));
6907     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6908     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6909     test_dm_messages(0, 0, 0);
6910
6911     memset(&dm_messages, 0, sizeof(dm_messages));
6912     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6913     ok(0 == r, "expected 0, got %d\n", r);
6914     test_dm_messages(0, 1, 0);
6915
6916     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6917     ok(2 == lcount, "expected 2, got %d\n", lcount);
6918
6919     memset(&dm_messages, 0, sizeof(dm_messages));
6920     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6921     ok(0 == r, "expected 0, got %d\n", r);
6922     test_dm_messages(0, 0, 0);
6923
6924     memset(&dm_messages, 0, sizeof(dm_messages));
6925     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6926     ok(0 == r, "expected 0, got %d\n", r);
6927     test_dm_messages(0, 0, 1);
6928
6929     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6930         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6931     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6932
6933     memset(&dm_messages, 0, sizeof(dm_messages));
6934     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6935     ok(0 == r, "expected 0, got %d\n", r);
6936     test_dm_messages(0, 1, 1);
6937
6938     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6939     ok(2 == lcount, "expected 2, got %d\n", lcount);
6940
6941     DestroyWindow(hwButton);
6942     DestroyWindow(hwRichEdit);
6943
6944     /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6945
6946     hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6947
6948     memset(&dm_messages, 0, sizeof(dm_messages));
6949     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6950     ok(0 == r, "expected 0, got %d\n", r);
6951     test_dm_messages(0, 0, 0);
6952
6953     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6954     ok(2 == lcount, "expected 2, got %d\n", lcount);
6955
6956     memset(&dm_messages, 0, sizeof(dm_messages));
6957     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6958     ok(0 == r, "expected 0, got %d\n", r);
6959     test_dm_messages(0, 0, 0);
6960
6961     memset(&dm_messages, 0, sizeof(dm_messages));
6962     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6963     ok(0 == r, "expected 0, got %d\n", r);
6964     test_dm_messages(0, 0, 0);
6965
6966     memset(&dm_messages, 0, sizeof(dm_messages));
6967     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6968     ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6969     test_dm_messages(0, 0, 0);
6970
6971     memset(&dm_messages, 0, sizeof(dm_messages));
6972     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6973     ok(0 == r, "expected 0, got %d\n", r);
6974     test_dm_messages(0, 0, 0);
6975
6976     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6977     ok(3 == lcount, "expected 3, got %d\n", lcount);
6978
6979     memset(&dm_messages, 0, sizeof(dm_messages));
6980     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6981     ok(0 == r, "expected 0, got %d\n", r);
6982     test_dm_messages(0, 0, 0);
6983
6984     memset(&dm_messages, 0, sizeof(dm_messages));
6985     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6986     ok(0 == r, "expected 0, got %d\n", r);
6987     test_dm_messages(0, 0, 1);
6988
6989     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6990         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6991     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6992
6993     memset(&dm_messages, 0, sizeof(dm_messages));
6994     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6995     ok(0 == r, "expected 0, got %d\n", r);
6996     test_dm_messages(0, 0, 0);
6997
6998     lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6999     ok(4 == lcount, "expected 4, got %d\n", lcount);
7000
7001     DestroyWindow(hwButton);
7002     DestroyWindow(hwRichEdit);
7003
7004     /* Check messages from richedit(0) */
7005
7006     hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7007
7008     memset(&dm_messages, 0, sizeof(dm_messages));
7009     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7010     ok(0 == r, "expected 0, got %d\n", r);
7011     test_dm_messages(0, 0, 0);
7012
7013     memset(&dm_messages, 0, sizeof(dm_messages));
7014     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7015     ok(0 == r, "expected 0, got %d\n", r);
7016     test_dm_messages(0, 0, 0);
7017
7018     memset(&dm_messages, 0, sizeof(dm_messages));
7019     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7020     ok(0 == r, "expected 0, got %d\n", r);
7021     test_dm_messages(0, 0, 0);
7022
7023     memset(&dm_messages, 0, sizeof(dm_messages));
7024     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7025     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7026     test_dm_messages(0, 0, 0);
7027
7028     memset(&dm_messages, 0, sizeof(dm_messages));
7029     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7030     ok(0 == r, "expected 0, got %d\n", r);
7031     test_dm_messages(0, 1, 0);
7032
7033     memset(&dm_messages, 0, sizeof(dm_messages));
7034     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7035     ok(0 == r, "expected 0, got %d\n", r);
7036     test_dm_messages(0, 0, 0);
7037
7038     memset(&dm_messages, 0, sizeof(dm_messages));
7039     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7040     ok(0 == r, "expected 0, got %d\n", r);
7041     test_dm_messages(0, 0, 1);
7042
7043     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7044         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7045     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7046
7047     memset(&dm_messages, 0, sizeof(dm_messages));
7048     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7049     ok(0 == r, "expected 0, got %d\n", r);
7050     test_dm_messages(0, 1, 1);
7051
7052     DestroyWindow(hwRichEdit);
7053
7054     /* Check messages from richedit(ES_WANTRETURN) */
7055
7056     hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7057
7058     memset(&dm_messages, 0, sizeof(dm_messages));
7059     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7060     ok(0 == r, "expected 0, got %d\n", r);
7061     test_dm_messages(0, 0, 0);
7062
7063     memset(&dm_messages, 0, sizeof(dm_messages));
7064     r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7065     ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7066     test_dm_messages(0, 0, 0);
7067
7068     memset(&dm_messages, 0, sizeof(dm_messages));
7069     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7070     ok(0 == r, "expected 0, got %d\n", r);
7071     test_dm_messages(0, 0, 0);
7072
7073     hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7074         100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7075     ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7076
7077     memset(&dm_messages, 0, sizeof(dm_messages));
7078     r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7079     ok(0 == r, "expected 0, got %d\n", r);
7080     test_dm_messages(0, 0, 0);
7081
7082     DestroyWindow(hwRichEdit);
7083     DestroyWindow(hwParent);
7084 }
7085
7086 static void test_EM_FINDWORDBREAK_W(void)
7087 {
7088     static const struct {
7089         WCHAR c;
7090         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
7091     } delimiter_tests[] = {
7092         {0x0a,   FALSE},         /* newline */
7093         {0x0b,   FALSE},         /* vertical tab */
7094         {0x0c,   FALSE},         /* form feed */
7095         {0x0d,   FALSE},         /* carriage return */
7096         {0x20,   TRUE},          /* space */
7097         {0x61,   FALSE},         /* capital letter a */
7098         {0xa0,   FALSE},         /* no-break space */
7099         {0x2000, FALSE},         /* en quad */
7100         {0x3000, FALSE},         /* Ideographic space */
7101         {0x1100, FALSE},         /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7102         {0x11ff, FALSE},         /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7103         {0x115f, FALSE},         /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7104         {0xac00, FALSE},         /* Hangul character GA*/
7105         {0xd7af, FALSE},         /* End of Hangul character chart */
7106         {0xf020, TRUE},          /* MS private for CP_SYMBOL round trip?, see kb897872 */
7107         {0xff20, FALSE},         /* fullwidth commercial @ */
7108         {WCH_EMBEDDING, FALSE},  /* object replacement character*/
7109     };
7110     int i;
7111     HWND hwndRichEdit = new_richeditW(NULL);
7112     ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7113     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7114     {
7115         WCHAR wbuf[2];
7116         int result;
7117
7118         wbuf[0] = delimiter_tests[i].c;
7119         wbuf[1] = 0;
7120         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7121         result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7122         if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7123             todo_wine
7124                 ok(result == delimiter_tests[i].isdelimiter,
7125                    "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7126                    delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7127         else
7128             ok(result == delimiter_tests[i].isdelimiter,
7129                "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7130                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7131     }
7132     DestroyWindow(hwndRichEdit);
7133 }
7134
7135 static void test_EM_FINDWORDBREAK_A(void)
7136 {
7137     static const struct {
7138         WCHAR c;
7139         BOOL isdelimiter;        /* expected result of WB_ISDELIMITER */
7140     } delimiter_tests[] = {
7141         {0x0a,   FALSE},         /* newline */
7142         {0x0b,   FALSE},         /* vertical tab */
7143         {0x0c,   FALSE},         /* form feed */
7144         {0x0d,   FALSE},         /* carriage return */
7145         {0x20,   TRUE},          /* space */
7146         {0x61,   FALSE},         /* capital letter a */
7147     };
7148     int i;
7149     HWND hwndRichEdit = new_richedit(NULL);
7150
7151     ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7152     for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7153     {
7154         int result;
7155         char buf[2];
7156         buf[0] = delimiter_tests[i].c;
7157         buf[1] = 0;
7158         SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7159         result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7160         if (buf[0] == 0x20)
7161             todo_wine
7162                 ok(result == delimiter_tests[i].isdelimiter,
7163                    "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7164                    delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7165         else
7166             ok(result == delimiter_tests[i].isdelimiter,
7167                "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7168                delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7169     }
7170     DestroyWindow(hwndRichEdit);
7171 }
7172
7173 /*
7174  * This test attempts to show the effect of enter on a richedit
7175  * control v1.0 inserts CRLF whereas for higher versions it only
7176  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7177  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7178  * does for higher. The same test is cloned in riched32 and riched20.
7179  */
7180 static void test_enter(void)
7181 {
7182     static const struct {
7183       const char *initialtext;
7184       const int   cursor;
7185       const char *expectedwmtext;
7186       const char *expectedemtext;
7187       const char *expectedemtextcrlf;
7188     } testenteritems[] = {
7189       { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7190       { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7191       { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7192       { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7193       { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7194     };
7195
7196   char expectedbuf[1024];
7197   char resultbuf[1024];
7198   HWND hwndRichEdit = new_richedit(NULL);
7199   UINT i,j;
7200
7201   for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7202
7203     char buf[1024] = {0};
7204     LRESULT result;
7205     GETTEXTEX getText;
7206     const char *expected;
7207
7208     /* Set the text to the initial text */
7209     result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
7210     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7211
7212     /* Send Enter */
7213     SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7214     simulate_typing_characters(hwndRichEdit, "\r");
7215
7216     /* 1. Retrieve with WM_GETTEXT */
7217     buf[0] = 0x00;
7218     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
7219     expected = testenteritems[i].expectedwmtext;
7220
7221     resultbuf[0]=0x00;
7222     for (j = 0; j < (UINT)result; j++)
7223       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7224     expectedbuf[0] = '\0';
7225     for (j = 0; j < strlen(expected); j++)
7226       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7227
7228     result = strcmp(expected, buf);
7229     ok (result == 0,
7230         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7231         i, resultbuf, expectedbuf);
7232
7233     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7234     getText.cb = sizeof(buf);
7235     getText.flags = GT_DEFAULT;
7236     getText.codepage      = CP_ACP;
7237     getText.lpDefaultChar = NULL;
7238     getText.lpUsedDefChar = NULL;
7239     buf[0] = 0x00;
7240     result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7241     expected = testenteritems[i].expectedemtext;
7242
7243     resultbuf[0]=0x00;
7244     for (j = 0; j < (UINT)result; j++)
7245       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7246     expectedbuf[0] = '\0';
7247     for (j = 0; j < strlen(expected); j++)
7248       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7249
7250     result = strcmp(expected, buf);
7251     ok (result == 0,
7252         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7253         i, resultbuf, expectedbuf);
7254
7255     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7256     getText.cb = sizeof(buf);
7257     getText.flags = GT_USECRLF;
7258     getText.codepage      = CP_ACP;
7259     getText.lpDefaultChar = NULL;
7260     getText.lpUsedDefChar = NULL;
7261     buf[0] = 0x00;
7262     result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7263     expected = testenteritems[i].expectedemtextcrlf;
7264
7265     resultbuf[0]=0x00;
7266     for (j = 0; j < (UINT)result; j++)
7267       sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7268     expectedbuf[0] = '\0';
7269     for (j = 0; j < strlen(expected); j++)
7270       sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7271
7272     result = strcmp(expected, buf);
7273     ok (result == 0,
7274         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7275         i, resultbuf, expectedbuf);
7276   }
7277
7278   DestroyWindow(hwndRichEdit);
7279 }
7280
7281 START_TEST( editor )
7282 {
7283   BOOL ret;
7284   /* Must explicitly LoadLibrary(). The test has no references to functions in
7285    * RICHED20.DLL, so the linker doesn't actually link to it. */
7286   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7287   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7288
7289   test_WM_CHAR();
7290   test_EM_FINDTEXT();
7291   test_EM_GETLINE();
7292   test_EM_POSFROMCHAR();
7293   test_EM_SCROLLCARET();
7294   test_EM_SCROLL();
7295   test_scrollbar_visibility();
7296   test_WM_SETTEXT();
7297   test_EM_LINELENGTH();
7298   test_EM_SETCHARFORMAT();
7299   test_EM_SETTEXTMODE();
7300   test_TM_PLAINTEXT();
7301   test_EM_SETOPTIONS();
7302   test_WM_GETTEXT();
7303   test_EM_GETTEXTRANGE();
7304   test_EM_GETSELTEXT();
7305   test_EM_SETUNDOLIMIT();
7306   test_ES_PASSWORD();
7307   test_EM_SETTEXTEX();
7308   test_EM_LIMITTEXT();
7309   test_EM_EXLIMITTEXT();
7310   test_EM_GETLIMITTEXT();
7311   test_WM_SETFONT();
7312   test_EM_GETMODIFY();
7313   test_EM_EXSETSEL();
7314   test_WM_PASTE();
7315   test_EM_STREAMIN();
7316   test_EM_STREAMOUT();
7317   test_EM_STREAMOUT_FONTTBL();
7318   test_EM_StreamIn_Undo();
7319   test_EM_FORMATRANGE();
7320   test_unicode_conversions();
7321   test_EM_GETTEXTLENGTHEX();
7322   test_EM_REPLACESEL(1);
7323   test_EM_REPLACESEL(0);
7324   test_WM_NOTIFY();
7325   test_EM_AUTOURLDETECT();
7326   test_eventMask();
7327   test_undo_coalescing();
7328   test_word_movement();
7329   test_EM_CHARFROMPOS();
7330   test_SETPARAFORMAT();
7331   test_word_wrap();
7332   test_autoscroll();
7333   test_format_rect();
7334   test_WM_GETDLGCODE();
7335   test_zoom();
7336   test_dialogmode();
7337   test_EM_FINDWORDBREAK_W();
7338   test_EM_FINDWORDBREAK_A();
7339   test_enter();
7340
7341   /* Set the environment variable WINETEST_RICHED20 to keep windows
7342    * responsive and open for 30 seconds. This is useful for debugging.
7343    */
7344   if (getenv( "WINETEST_RICHED20" )) {
7345     keep_responsive(30);
7346   }
7347
7348   OleFlushClipboard();
7349   ret = FreeLibrary(hmoduleRichEdit);
7350   ok(ret, "error: %d\n", (int) GetLastError());
7351 }