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