riched20/tests: Remove the todo_wine logic where appropriate.
[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     /* cpMax of text length is used instead of -1 in this case */
1424     textRange.lpstrText = buffer;
1425     textRange.chrg.cpMin = 0;
1426     textRange.chrg.cpMax = -1;
1427     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1428     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1429     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1430
1431     /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1432     textRange.lpstrText = buffer;
1433     textRange.chrg.cpMin = -1;
1434     textRange.chrg.cpMax = 1;
1435     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1436     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1437     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1438
1439     /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1440     textRange.lpstrText = buffer;
1441     textRange.chrg.cpMin = 1;
1442     textRange.chrg.cpMax = -1;
1443     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1444     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1445     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446
1447     /* no end character is copied if cpMax - cpMin < 0 */
1448     textRange.lpstrText = buffer;
1449     textRange.chrg.cpMin = 5;
1450     textRange.chrg.cpMax = 5;
1451     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1452     ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1453     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454
1455     /* cpMax of text length is used if cpMax > text length*/
1456     textRange.lpstrText = buffer;
1457     textRange.chrg.cpMin = 0;
1458     textRange.chrg.cpMax = 1000;
1459     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1460     ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1461     ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1462
1463     DestroyWindow(hwndRichEdit);
1464 }
1465
1466 static void test_EM_GETSELTEXT(void)
1467 {
1468     HWND hwndRichEdit = new_richedit(NULL);
1469     const char * text1 = "foo bar\r\nfoo bar";
1470     const char * text2 = "foo bar\rfoo bar";
1471     const char * expect = "bar\rfoo";
1472     char buffer[1024] = {0};
1473     LRESULT result;
1474
1475     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1476
1477     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1478     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1479     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1481
1482     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1483
1484     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1485     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1486     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488
1489     DestroyWindow(hwndRichEdit);
1490 }
1491
1492 /* FIXME: need to test unimplemented options and robustly test wparam */
1493 static void test_EM_SETOPTIONS(void)
1494 {
1495     HWND hwndRichEdit;
1496     static const char text[] = "Hello. My name is RichEdit!";
1497     char buffer[1024] = {0};
1498     DWORD dwStyle, options, oldOptions;
1499     DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1500                          ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1501                          ES_SELECTIONBAR|ES_VERTICAL;
1502
1503     /* Test initial options. */
1504     hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1505                                 0, 0, 200, 60, NULL, NULL,
1506                                 hmoduleRichEdit, NULL);
1507     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1508        RICHEDIT_CLASS, (int) GetLastError());
1509     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1510     ok(options == 0, "Incorrect initial options %x\n", options);
1511     DestroyWindow(hwndRichEdit);
1512
1513     hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1514                                 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1515                                 0, 0, 200, 60, NULL, NULL,
1516                                 hmoduleRichEdit, NULL);
1517     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1518        RICHEDIT_CLASS, (int) GetLastError());
1519     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1520     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1521     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1522        "Incorrect initial options %x\n", options);
1523
1524     /* NEGATIVE TESTING - NO OPTIONS SET */
1525     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1526     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1527
1528     /* testing no readonly by sending 'a' to the control*/
1529     SetFocus(hwndRichEdit);
1530     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1531     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1532     ok(buffer[0]=='a', 
1533        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1534     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1535
1536     /* READONLY - sending 'a' to the control */
1537     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1538     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1539     SetFocus(hwndRichEdit);
1540     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1541     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1542     ok(buffer[0]==text[0], 
1543        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
1544
1545     /* EM_SETOPTIONS changes the window style, but changing the
1546      * window style does not change the options. */
1547     dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1548     ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1549     SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1550     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1551     ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1552     /* Confirm that the text is still read only. */
1553     SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1554     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555     ok(buffer[0]==text[0],
1556        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1557
1558     oldOptions = options;
1559     SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1560     options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1561     ok(options == oldOptions,
1562        "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1563
1564     DestroyWindow(hwndRichEdit);
1565 }
1566
1567 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1568 {
1569   CHARFORMAT2W text_format;
1570   text_format.cbSize = sizeof(text_format);
1571   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1572   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1573   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1574 }
1575
1576 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1577 {
1578   int link_present = 0;
1579
1580   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1581   if (is_url) 
1582   { /* control text is url; should get CFE_LINK */
1583         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1584   }
1585   else 
1586   {
1587     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1588   }
1589 }
1590
1591 static HWND new_static_wnd(HWND parent) {
1592   return new_window("Static", 0, parent);
1593 }
1594
1595 static void test_EM_AUTOURLDETECT(void)
1596 {
1597   /* DO NOT change the properties of the first two elements. To shorten the
1598      tests, all tests after WM_SETTEXT test just the first two elements -
1599      one non-URL and one URL */
1600   struct urls_s {
1601     const char *text;
1602     int is_url;
1603   } urls[12] = {
1604     {"winehq.org", 0},
1605     {"http://www.winehq.org", 1},
1606     {"http//winehq.org", 0},
1607     {"ww.winehq.org", 0},
1608     {"www.winehq.org", 1},
1609     {"ftp://192.168.1.1", 1},
1610     {"ftp//192.168.1.1", 0},
1611     {"mailto:your@email.com", 1},    
1612     {"prospero:prosperoserver", 1},
1613     {"telnet:test", 1},
1614     {"news:newserver", 1},
1615     {"wais:waisserver", 1}  
1616   };
1617
1618   int i, j;
1619   int urlRet=-1;
1620   HWND hwndRichEdit, parent;
1621
1622   /* All of the following should cause the URL to be detected  */
1623   const char * templates_delim[] = {
1624     "This is some text with X on it",
1625     "This is some text with (X) on it",
1626     "This is some text with X\r on it",
1627     "This is some text with ---X--- on it",
1628     "This is some text with \"X\" on it",
1629     "This is some text with 'X' on it",
1630     "This is some text with 'X' on it",
1631     "This is some text with :X: on it",
1632
1633     "This text ends with X",
1634
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     "This is some text with X: on it",
1640
1641     "This is some text with (X on it",
1642     "This is some text with \rX on it",
1643     "This is some text with ---X on it",
1644     "This is some text with \"X on it",
1645     "This is some text with 'X on it",
1646     "This is some text with :X on it",
1647   };
1648   /* None of these should cause the URL to be detected */
1649   const char * templates_non_delim[] = {
1650     "This is some text with |X| on it",
1651     "This is some text with *X* on it",
1652     "This is some text with /X/ on it",
1653     "This is some text with +X+ on it",
1654     "This is some text with %X% on it",
1655     "This is some text with #X# on it",
1656     "This is some text with @X@ on it",
1657     "This is some text with \\X\\ on it",
1658     "This is some text with |X on it",
1659     "This is some text with *X on it",
1660     "This is some text with /X on it",
1661     "This is some text with +X on it",
1662     "This is some text with %X on it",
1663     "This is some text with #X on it",
1664     "This is some text with @X on it",
1665     "This is some text with \\X on it",
1666   };
1667   /* All of these cause the URL detection to be extended by one more byte,
1668      thus demonstrating that the tested character is considered as part
1669      of the URL. */
1670   const char * templates_xten_delim[] = {
1671     "This is some text with X| on it",
1672     "This is some text with X* on it",
1673     "This is some text with X/ on it",
1674     "This is some text with X+ on it",
1675     "This is some text with X% on it",
1676     "This is some text with X# on it",
1677     "This is some text with X@ on it",
1678     "This is some text with X\\ on it",
1679   };
1680   char buffer[1024];
1681
1682   parent = new_static_wnd(NULL);
1683   hwndRichEdit = new_richedit(parent);
1684   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1685   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1686   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1687   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1688   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1689   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1690   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1691   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1692   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1693   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1694   /* for each url, check the text to see if CFE_LINK effect is present */
1695   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1696
1697     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1698     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1699     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1700
1701     /* Link detection should happen immediately upon WM_SETTEXT */
1702     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1703     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1704     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1705   }
1706   DestroyWindow(hwndRichEdit);
1707
1708   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1709   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710     hwndRichEdit = new_richedit(parent);
1711
1712     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1713       char * at_pos;
1714       int at_offset;
1715       int end_offset;
1716
1717       at_pos = strchr(templates_delim[j], 'X');
1718       at_offset = at_pos - templates_delim[j];
1719       strncpy(buffer, templates_delim[j], at_offset);
1720       buffer[at_offset] = '\0';
1721       strcat(buffer, urls[i].text);
1722       strcat(buffer, templates_delim[j] + at_offset + 1);
1723       end_offset = at_offset + strlen(urls[i].text);
1724
1725       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1726       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1727
1728       /* This assumes no templates start with the URL itself, and that they
1729          have at least two characters before the URL text */
1730       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1731         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1732       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1733         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1734       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1735         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1736
1737       if (urls[i].is_url)
1738       {
1739         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1740           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1741         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1742           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1743       }
1744       else
1745       {
1746         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1747           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1748         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1749           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1750       }
1751       if (buffer[end_offset] != '\0')
1752       {
1753         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1754           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1755         if (buffer[end_offset +1] != '\0')
1756         {
1757           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1758             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1759         }
1760       }
1761     }
1762
1763     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1764       char * at_pos;
1765       int at_offset;
1766       int end_offset;
1767
1768       at_pos = strchr(templates_non_delim[j], 'X');
1769       at_offset = at_pos - templates_non_delim[j];
1770       strncpy(buffer, templates_non_delim[j], at_offset);
1771       buffer[at_offset] = '\0';
1772       strcat(buffer, urls[i].text);
1773       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1774       end_offset = at_offset + strlen(urls[i].text);
1775
1776       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1777       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1778
1779       /* This assumes no templates start with the URL itself, and that they
1780          have at least two characters before the URL text */
1781       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1782         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1783       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1784         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1785       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1786         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1787
1788       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1789         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1790       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1791         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1792       if (buffer[end_offset] != '\0')
1793       {
1794         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1795           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1796         if (buffer[end_offset +1] != '\0')
1797         {
1798           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1799             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1800         }
1801       }
1802     }
1803
1804     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1805       char * at_pos;
1806       int at_offset;
1807       int end_offset;
1808
1809       at_pos = strchr(templates_xten_delim[j], 'X');
1810       at_offset = at_pos - templates_xten_delim[j];
1811       strncpy(buffer, templates_xten_delim[j], at_offset);
1812       buffer[at_offset] = '\0';
1813       strcat(buffer, urls[i].text);
1814       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1815       end_offset = at_offset + strlen(urls[i].text);
1816
1817       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1818       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1819
1820       /* This assumes no templates start with the URL itself, and that they
1821          have at least two characters before the URL text */
1822       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1823         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1824       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1825         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1826       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1827         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1828
1829       if (urls[i].is_url)
1830       {
1831         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1832           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1833         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1834           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1835         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1836           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1837       }
1838       else
1839       {
1840         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1841           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1842         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1843           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1845           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1846       }
1847       if (buffer[end_offset +1] != '\0')
1848       {
1849         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1850           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1851         if (buffer[end_offset +2] != '\0')
1852         {
1853           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1854             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1855         }
1856       }
1857     }
1858
1859     DestroyWindow(hwndRichEdit);
1860     hwndRichEdit = NULL;
1861   }
1862
1863   /* Test detection of URLs within normal text - WM_CHAR case. */
1864   /* Test only the first two URL examples for brevity */
1865   for (i = 0; i < 2; i++) {
1866     hwndRichEdit = new_richedit(parent);
1867
1868     /* Also for brevity, test only the first three delimiters */
1869     for (j = 0; j < 3; j++) {
1870       char * at_pos;
1871       int at_offset;
1872       int end_offset;
1873       int u, v;
1874
1875       at_pos = strchr(templates_delim[j], 'X');
1876       at_offset = at_pos - templates_delim[j];
1877       end_offset = at_offset + strlen(urls[i].text);
1878
1879       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1880       SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1881       for (u = 0; templates_delim[j][u]; u++) {
1882         if (templates_delim[j][u] == '\r') {
1883           simulate_typing_characters(hwndRichEdit, "\r");
1884         } else if (templates_delim[j][u] != 'X') {
1885           SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1886         } else {
1887           for (v = 0; urls[i].text[v]; v++) {
1888             SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1889           }
1890         }
1891       }
1892       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1893
1894       /* This assumes no templates start with the URL itself, and that they
1895          have at least two characters before the URL text */
1896       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1897         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1898       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1899         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1900       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1901         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1902
1903       if (urls[i].is_url)
1904       {
1905         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1906           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1907         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1908           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1909       }
1910       else
1911       {
1912         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1914         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1916       }
1917       if (buffer[end_offset] != '\0')
1918       {
1919         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1920           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1921         if (buffer[end_offset +1] != '\0')
1922         {
1923           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1924             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1925         }
1926       }
1927
1928       /* The following will insert a paragraph break after the first character
1929          of the URL candidate, thus breaking the URL. It is expected that the
1930          CFE_LINK attribute should break across both pieces of the URL */
1931       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1932       simulate_typing_characters(hwndRichEdit, "\r");
1933       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1934
1935       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1936         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1937       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1938         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1939       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1940         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1941
1942       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1943         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1944       /* end_offset moved because of paragraph break */
1945       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1946         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1947       ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1948       if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
1949       {
1950         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1951           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1952         if (buffer[end_offset +2] != '\0')
1953         {
1954           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1955             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1956         }
1957       }
1958
1959       /* The following will remove the just-inserted paragraph break, thus
1960          restoring the URL */
1961       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1962       simulate_typing_characters(hwndRichEdit, "\b");
1963       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1964
1965       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1966         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1967       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1968         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1969       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1970         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1971
1972       if (urls[i].is_url)
1973       {
1974         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1975           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1976         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1977           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1978       }
1979       else
1980       {
1981         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1982           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1983         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1984           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1985       }
1986       if (buffer[end_offset] != '\0')
1987       {
1988         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1989           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1990         if (buffer[end_offset +1] != '\0')
1991         {
1992           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1993             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1994         }
1995       }
1996     }
1997     DestroyWindow(hwndRichEdit);
1998     hwndRichEdit = NULL;
1999   }
2000
2001   /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2002   /* Test just the first two URL examples for brevity */
2003   for (i = 0; i < 2; i++) {
2004     SETTEXTEX st;
2005
2006     hwndRichEdit = new_richedit(parent);
2007
2008     /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2009        be detected:
2010        1) Set entire text, a la WM_SETTEXT
2011        2) Set a selection of the text to the URL
2012        3) Set a portion of the text at a time, which eventually results in
2013           an URL
2014        All of them should give equivalent results
2015      */
2016
2017     /* Set entire text in one go, like WM_SETTEXT */
2018     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2019       char * at_pos;
2020       int at_offset;
2021       int end_offset;
2022
2023       st.codepage = CP_ACP;
2024       st.flags = ST_DEFAULT;
2025
2026       at_pos = strchr(templates_delim[j], 'X');
2027       at_offset = at_pos - templates_delim[j];
2028       strncpy(buffer, templates_delim[j], at_offset);
2029       buffer[at_offset] = '\0';
2030       strcat(buffer, urls[i].text);
2031       strcat(buffer, templates_delim[j] + at_offset + 1);
2032       end_offset = at_offset + strlen(urls[i].text);
2033
2034       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2035       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2036
2037       /* This assumes no templates start with the URL itself, and that they
2038          have at least two characters before the URL text */
2039       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2040         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2041       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2042         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2043       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2044         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2045
2046       if (urls[i].is_url)
2047       {
2048         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2049           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2050         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2052       }
2053       else
2054       {
2055         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2056           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2057         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2058           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2059       }
2060       if (buffer[end_offset] != '\0')
2061       {
2062         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2063           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2064         if (buffer[end_offset +1] != '\0')
2065         {
2066           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2067             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2068         }
2069       }
2070     }
2071
2072     /* Set selection with X to the URL */
2073     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2074       char * at_pos;
2075       int at_offset;
2076       int end_offset;
2077
2078       at_pos = strchr(templates_delim[j], 'X');
2079       at_offset = at_pos - templates_delim[j];
2080       end_offset = at_offset + strlen(urls[i].text);
2081
2082       st.codepage = CP_ACP;
2083       st.flags = ST_DEFAULT;
2084       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2085       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2086       st.flags = ST_SELECTION;
2087       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2088       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2089       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2090
2091       /* This assumes no templates start with the URL itself, and that they
2092          have at least two characters before the URL text */
2093       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2094         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2095       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2096         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2097       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2098         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2099
2100       if (urls[i].is_url)
2101       {
2102         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2103           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2104         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2105           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2106       }
2107       else
2108       {
2109         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2110           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2111         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2112           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2113       }
2114       if (buffer[end_offset] != '\0')
2115       {
2116         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2117           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2118         if (buffer[end_offset +1] != '\0')
2119         {
2120           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2121             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2122         }
2123       }
2124     }
2125
2126     /* Set selection with X to the first character of the URL, then the rest */
2127     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2128       char * at_pos;
2129       int at_offset;
2130       int end_offset;
2131
2132       at_pos = strchr(templates_delim[j], 'X');
2133       at_offset = at_pos - templates_delim[j];
2134       end_offset = at_offset + strlen(urls[i].text);
2135
2136       strcpy(buffer, "YY");
2137       buffer[0] = urls[i].text[0];
2138
2139       st.codepage = CP_ACP;
2140       st.flags = ST_DEFAULT;
2141       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2143       st.flags = ST_SELECTION;
2144       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2146       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2147       SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2148       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2149
2150       /* This assumes no templates start with the URL itself, and that they
2151          have at least two characters before the URL text */
2152       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2153         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2154       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2155         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2156       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2157         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2158
2159       if (urls[i].is_url)
2160       {
2161         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2163         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165       }
2166       else
2167       {
2168         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2169           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2170         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2171           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2172       }
2173       if (buffer[end_offset] != '\0')
2174       {
2175         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2176           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2177         if (buffer[end_offset +1] != '\0')
2178         {
2179           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2180             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2181         }
2182       }
2183     }
2184
2185     DestroyWindow(hwndRichEdit);
2186     hwndRichEdit = NULL;
2187   }
2188
2189   /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2190   /* Test just the first two URL examples for brevity */
2191   for (i = 0; i < 2; i++) {
2192     hwndRichEdit = new_richedit(parent);
2193
2194     /* Set selection with X to the URL */
2195     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2196       char * at_pos;
2197       int at_offset;
2198       int end_offset;
2199
2200       at_pos = strchr(templates_delim[j], 'X');
2201       at_offset = at_pos - templates_delim[j];
2202       end_offset = at_offset + strlen(urls[i].text);
2203
2204       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2205       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2206       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2207       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2208       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2209
2210       /* This assumes no templates start with the URL itself, and that they
2211          have at least two characters before the URL text */
2212       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2213         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2214       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2215         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2216       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2217         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2218
2219       if (urls[i].is_url)
2220       {
2221         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2222           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2223         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2224           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2225       }
2226       else
2227       {
2228         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2229           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2230         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2231           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2232       }
2233       if (buffer[end_offset] != '\0')
2234       {
2235         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2236           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2237         if (buffer[end_offset +1] != '\0')
2238         {
2239           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2240             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2241         }
2242       }
2243     }
2244
2245     /* Set selection with X to the first character of the URL, then the rest */
2246     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2247       char * at_pos;
2248       int at_offset;
2249       int end_offset;
2250
2251       at_pos = strchr(templates_delim[j], 'X');
2252       at_offset = at_pos - templates_delim[j];
2253       end_offset = at_offset + strlen(urls[i].text);
2254
2255       strcpy(buffer, "YY");
2256       buffer[0] = urls[i].text[0];
2257
2258       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2259       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2260       SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2261       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2262       SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2263       SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2264       SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2265
2266       /* This assumes no templates start with the URL itself, and that they
2267          have at least two characters before the URL text */
2268       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2269         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2270       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2271         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2272       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2273         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2274
2275       if (urls[i].is_url)
2276       {
2277         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2278           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2279         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2280           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2281       }
2282       else
2283       {
2284         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2285           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2286         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2287           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2288       }
2289       if (buffer[end_offset] != '\0')
2290       {
2291         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2292           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2293         if (buffer[end_offset +1] != '\0')
2294         {
2295           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2296             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2297         }
2298       }
2299     }
2300
2301     DestroyWindow(hwndRichEdit);
2302     hwndRichEdit = NULL;
2303   }
2304
2305   DestroyWindow(parent);
2306 }
2307
2308 static void test_EM_SCROLL(void)
2309 {
2310   int i, j;
2311   int r; /* return value */
2312   int expr; /* expected return value */
2313   HWND hwndRichEdit = new_richedit(NULL);
2314   int y_before, y_after; /* units of lines of text */
2315
2316   /* test a richedit box containing a single line of text */
2317   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2318   expr = 0x00010000;
2319   for (i = 0; i < 4; i++) {
2320     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2321
2322     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2323     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2325        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2326     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2327        "(i == %d)\n", y_after, i);
2328   }
2329
2330   /*
2331    * test a richedit box that will scroll. There are two general
2332    * cases: the case without any long lines and the case with a long
2333    * line.
2334    */
2335   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2336     if (i == 0)
2337       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2338     else
2339       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2340                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2341                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2342                   "LONG LINE \nb\nc\nd\ne");
2343     for (j = 0; j < 12; j++) /* reset scroll position to top */
2344       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2345
2346     /* get first visible line */
2347     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2348     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2349
2350     /* get new current first visible line */
2351     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2352
2353     ok(((r & 0xffffff00) == 0x00010000) &&
2354        ((r & 0x000000ff) != 0x00000000),
2355        "EM_SCROLL page down didn't scroll by a small positive number of "
2356        "lines (r == 0x%08x)\n", r);
2357     ok(y_after > y_before, "EM_SCROLL page down not functioning "
2358        "(line %d scrolled to line %d\n", y_before, y_after);
2359
2360     y_before = y_after;
2361     
2362     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2363     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2364     ok(((r & 0xffffff00) == 0x0001ff00),
2365        "EM_SCROLL page up didn't scroll by a small negative number of lines "
2366        "(r == 0x%08x)\n", r);
2367     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2368        "%d scrolled to line %d\n", y_before, y_after);
2369     
2370     y_before = y_after;
2371
2372     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2373
2374     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2375
2376     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2377        "(r == 0x%08x)\n", r);
2378     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2379        "1 line (%d scrolled to %d)\n", y_before, y_after);
2380
2381     y_before = y_after;
2382
2383     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2384
2385     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2386
2387     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2388        "(r == 0x%08x)\n", r);
2389     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2390        "line (%d scrolled to %d)\n", y_before, y_after);
2391
2392     y_before = y_after;
2393
2394     r = SendMessage(hwndRichEdit, EM_SCROLL,
2395                     SB_LINEUP, 0); /* lineup beyond top */
2396
2397     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2398
2399     ok(r == 0x00010000,
2400        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2401     ok(y_before == y_after,
2402        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2403
2404     y_before = y_after;
2405
2406     r = SendMessage(hwndRichEdit, EM_SCROLL,
2407                     SB_PAGEUP, 0);/*page up beyond top */
2408
2409     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2410
2411     ok(r == 0x00010000,
2412        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2413     ok(y_before == y_after,
2414        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2415
2416     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2417       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2418     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2419     r = SendMessage(hwndRichEdit, EM_SCROLL,
2420                     SB_PAGEDOWN, 0); /* page down beyond bot */
2421     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2422
2423     ok(r == 0x00010000,
2424        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2425     ok(y_before == y_after,
2426        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2427        y_before, y_after);
2428
2429     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2430     SendMessage(hwndRichEdit, EM_SCROLL,
2431                 SB_LINEDOWN, 0); /* line down beyond bot */
2432     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2433     
2434     ok(r == 0x00010000,
2435        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2436     ok(y_before == y_after,
2437        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2438        y_before, y_after);
2439   }
2440   DestroyWindow(hwndRichEdit);
2441 }
2442
2443 unsigned int recursionLevel = 0;
2444 unsigned int WM_SIZE_recursionLevel = 0;
2445 BOOL bailedOutOfRecursion = FALSE;
2446 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2447
2448 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2449 {
2450     LRESULT r;
2451
2452     if (bailedOutOfRecursion) return 0;
2453     if (recursionLevel >= 32) {
2454         bailedOutOfRecursion = TRUE;
2455         return 0;
2456     }
2457
2458     recursionLevel++;
2459     switch (message) {
2460     case WM_SIZE:
2461         WM_SIZE_recursionLevel++;
2462         r = richeditProc(hwnd, message, wParam, lParam);
2463         /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2464         ShowScrollBar(hwnd, SB_VERT, TRUE);
2465         WM_SIZE_recursionLevel--;
2466         break;
2467     default:
2468         r = richeditProc(hwnd, message, wParam, lParam);
2469         break;
2470     }
2471     recursionLevel--;
2472     return r;
2473 }
2474
2475 static void test_scrollbar_visibility(void)
2476 {
2477   HWND hwndRichEdit;
2478   const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2479   SCROLLINFO si;
2480   WNDCLASSA cls;
2481   BOOL r;
2482
2483   /* These tests show that richedit should temporarily refrain from automatically
2484      hiding or showing its scrollbars (vertical at least) when an explicit request
2485      is made via ShowScrollBar() or similar, outside of standard richedit logic.
2486      Some applications depend on forced showing (when otherwise richedit would
2487      hide the vertical scrollbar) and are thrown on an endless recursive loop
2488      if richedit auto-hides the scrollbar again. Apparently they never heard of
2489      the ES_DISABLENOSCROLL style... */
2490
2491   hwndRichEdit = new_richedit(NULL);
2492
2493   /* Test default scrollbar visibility behavior */
2494   memset(&si, 0, sizeof(si));
2495   si.cbSize = sizeof(si);
2496   si.fMask = SIF_PAGE | SIF_RANGE;
2497   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2498   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2499     "Vertical scrollbar is visible, should be invisible.\n");
2500   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2501         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2502         si.nPage, si.nMin, si.nMax);
2503
2504   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2505   memset(&si, 0, sizeof(si));
2506   si.cbSize = sizeof(si);
2507   si.fMask = SIF_PAGE | SIF_RANGE;
2508   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2510     "Vertical scrollbar is visible, should be invisible.\n");
2511   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2512         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2513         si.nPage, si.nMin, si.nMax);
2514
2515   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2516   memset(&si, 0, sizeof(si));
2517   si.cbSize = sizeof(si);
2518   si.fMask = SIF_PAGE | SIF_RANGE;
2519   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2520   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2521     "Vertical scrollbar is invisible, should be visible.\n");
2522   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2523         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2524         si.nPage, si.nMin, si.nMax);
2525
2526   /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2527      even though it hides the scrollbar */
2528   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2529   memset(&si, 0, sizeof(si));
2530   si.cbSize = sizeof(si);
2531   si.fMask = SIF_PAGE | SIF_RANGE;
2532   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2534     "Vertical scrollbar is visible, should be invisible.\n");
2535   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2536         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537         si.nPage, si.nMin, si.nMax);
2538
2539   /* Setting non-scrolling text again does *not* reset scrollbar range */
2540   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2541   memset(&si, 0, sizeof(si));
2542   si.cbSize = sizeof(si);
2543   si.fMask = SIF_PAGE | SIF_RANGE;
2544   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546     "Vertical scrollbar is visible, should be invisible.\n");
2547   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549         si.nPage, si.nMin, si.nMax);
2550
2551   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552   memset(&si, 0, sizeof(si));
2553   si.cbSize = sizeof(si);
2554   si.fMask = SIF_PAGE | SIF_RANGE;
2555   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2557     "Vertical scrollbar is visible, should be invisible.\n");
2558   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560         si.nPage, si.nMin, si.nMax);
2561
2562   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2563   memset(&si, 0, sizeof(si));
2564   si.cbSize = sizeof(si);
2565   si.fMask = SIF_PAGE | SIF_RANGE;
2566   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2567   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2568     "Vertical scrollbar is visible, should be invisible.\n");
2569   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2570         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2571         si.nPage, si.nMin, si.nMax);
2572
2573   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2574   memset(&si, 0, sizeof(si));
2575   si.cbSize = sizeof(si);
2576   si.fMask = SIF_PAGE | SIF_RANGE;
2577   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2578   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2579     "Vertical scrollbar is visible, should be invisible.\n");
2580   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2581         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2582         si.nPage, si.nMin, si.nMax);
2583
2584   DestroyWindow(hwndRichEdit);
2585
2586   /* Test again, with ES_DISABLENOSCROLL style */
2587   hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2588
2589   /* Test default scrollbar visibility behavior */
2590   memset(&si, 0, sizeof(si));
2591   si.cbSize = sizeof(si);
2592   si.fMask = SIF_PAGE | SIF_RANGE;
2593   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595     "Vertical scrollbar is invisible, should be visible.\n");
2596   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2597         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2598         si.nPage, si.nMin, si.nMax);
2599
2600   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601   memset(&si, 0, sizeof(si));
2602   si.cbSize = sizeof(si);
2603   si.fMask = SIF_PAGE | SIF_RANGE;
2604   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2606     "Vertical scrollbar is invisible, should be visible.\n");
2607   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2608         "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2609         si.nPage, si.nMin, si.nMax);
2610
2611   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2612   memset(&si, 0, sizeof(si));
2613   si.cbSize = sizeof(si);
2614   si.fMask = SIF_PAGE | SIF_RANGE;
2615   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2617     "Vertical scrollbar is invisible, should be visible.\n");
2618   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2619         "reported page/range is %d (%d..%d)\n",
2620         si.nPage, si.nMin, si.nMax);
2621
2622   /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2623   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624   memset(&si, 0, sizeof(si));
2625   si.cbSize = sizeof(si);
2626   si.fMask = SIF_PAGE | SIF_RANGE;
2627   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629     "Vertical scrollbar is invisible, should be visible.\n");
2630   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632         si.nPage, si.nMin, si.nMax);
2633
2634   /* Setting non-scrolling text again does *not* reset scrollbar range */
2635   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2636   memset(&si, 0, sizeof(si));
2637   si.cbSize = sizeof(si);
2638   si.fMask = SIF_PAGE | SIF_RANGE;
2639   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641     "Vertical scrollbar is invisible, should be visible.\n");
2642   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644         si.nPage, si.nMin, si.nMax);
2645
2646   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2647   memset(&si, 0, sizeof(si));
2648   si.cbSize = sizeof(si);
2649   si.fMask = SIF_PAGE | SIF_RANGE;
2650   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2651   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2652     "Vertical scrollbar is invisible, should be visible.\n");
2653   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2654         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2655         si.nPage, si.nMin, si.nMax);
2656
2657   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2658   memset(&si, 0, sizeof(si));
2659   si.cbSize = sizeof(si);
2660   si.fMask = SIF_PAGE | SIF_RANGE;
2661   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2662   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2663     "Vertical scrollbar is invisible, should be visible.\n");
2664   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2665         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2666         si.nPage, si.nMin, si.nMax);
2667
2668   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2669   memset(&si, 0, sizeof(si));
2670   si.cbSize = sizeof(si);
2671   si.fMask = SIF_PAGE | SIF_RANGE;
2672   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2673   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2674     "Vertical scrollbar is invisible, should be visible.\n");
2675   ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2676         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2677         si.nPage, si.nMin, si.nMax);
2678
2679   DestroyWindow(hwndRichEdit);
2680
2681   /* Test behavior with explicit visibility request, using ShowScrollBar() */
2682   hwndRichEdit = new_richedit(NULL);
2683
2684   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2685   ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2686   memset(&si, 0, sizeof(si));
2687   si.cbSize = sizeof(si);
2688   si.fMask = SIF_PAGE | SIF_RANGE;
2689   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2690   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2691     "Vertical scrollbar is invisible, should be visible.\n");
2692   todo_wine {
2693   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2694         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2695         si.nPage, si.nMin, si.nMax);
2696   }
2697
2698   /* Ditto, see above */
2699   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2700   memset(&si, 0, sizeof(si));
2701   si.cbSize = sizeof(si);
2702   si.fMask = SIF_PAGE | SIF_RANGE;
2703   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2704   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2705     "Vertical scrollbar is invisible, should be visible.\n");
2706   todo_wine {
2707   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2708         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2709         si.nPage, si.nMin, si.nMax);
2710   }
2711
2712   /* Ditto, see above */
2713   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714   memset(&si, 0, sizeof(si));
2715   si.cbSize = sizeof(si);
2716   si.fMask = SIF_PAGE | SIF_RANGE;
2717   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719     "Vertical scrollbar is invisible, should be visible.\n");
2720   todo_wine {
2721   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2722         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2723         si.nPage, si.nMin, si.nMax);
2724   }
2725
2726   /* Ditto, see above */
2727   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2728   memset(&si, 0, sizeof(si));
2729   si.cbSize = sizeof(si);
2730   si.fMask = SIF_PAGE | SIF_RANGE;
2731   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2732   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2733     "Vertical scrollbar is invisible, should be visible.\n");
2734   todo_wine {
2735   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2736         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2737         si.nPage, si.nMin, si.nMax);
2738   }
2739
2740   /* Ditto, see above */
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 invisible, should be visible.\n");
2748   todo_wine {
2749   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751         si.nPage, si.nMin, si.nMax);
2752   }
2753
2754   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2755   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756   memset(&si, 0, sizeof(si));
2757   si.cbSize = sizeof(si);
2758   si.fMask = SIF_PAGE | SIF_RANGE;
2759   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2761     "Vertical scrollbar is visible, should be invisible.\n");
2762   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2763         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2764         si.nPage, si.nMin, si.nMax);
2765
2766   DestroyWindow(hwndRichEdit);
2767
2768   hwndRichEdit = new_richedit(NULL);
2769
2770   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2771   memset(&si, 0, sizeof(si));
2772   si.cbSize = sizeof(si);
2773   si.fMask = SIF_PAGE | SIF_RANGE;
2774   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2775   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2776     "Vertical scrollbar is visible, should be invisible.\n");
2777   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2778         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2779         si.nPage, si.nMin, si.nMax);
2780
2781   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2782   memset(&si, 0, sizeof(si));
2783   si.cbSize = sizeof(si);
2784   si.fMask = SIF_PAGE | SIF_RANGE;
2785   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2786   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2787     "Vertical scrollbar is visible, should be invisible.\n");
2788   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2789         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2790         si.nPage, si.nMin, si.nMax);
2791
2792   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2793   memset(&si, 0, sizeof(si));
2794   si.cbSize = sizeof(si);
2795   si.fMask = SIF_PAGE | SIF_RANGE;
2796   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2797   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2798     "Vertical scrollbar is visible, should be invisible.\n");
2799   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2800         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2801         si.nPage, si.nMin, si.nMax);
2802
2803   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2804   memset(&si, 0, sizeof(si));
2805   si.cbSize = sizeof(si);
2806   si.fMask = SIF_PAGE | SIF_RANGE;
2807   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809     "Vertical scrollbar is visible, should be invisible.\n");
2810   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2811         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2812         si.nPage, si.nMin, si.nMax);
2813
2814   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2815   memset(&si, 0, sizeof(si));
2816   si.cbSize = sizeof(si);
2817   si.fMask = SIF_PAGE | SIF_RANGE;
2818   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2820     "Vertical scrollbar is invisible, should be visible.\n");
2821   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2822         "reported page/range is %d (%d..%d)\n",
2823         si.nPage, si.nMin, si.nMax);
2824
2825   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2826   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827   memset(&si, 0, sizeof(si));
2828   si.cbSize = sizeof(si);
2829   si.fMask = SIF_PAGE | SIF_RANGE;
2830   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832     "Vertical scrollbar is visible, should be invisible.\n");
2833   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834         "reported page/range is %d (%d..%d)\n",
2835         si.nPage, si.nMin, si.nMax);
2836
2837   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838   memset(&si, 0, sizeof(si));
2839   si.cbSize = sizeof(si);
2840   si.fMask = SIF_PAGE | SIF_RANGE;
2841   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843     "Vertical scrollbar is visible, should be invisible.\n");
2844   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2845         "reported page/range is %d (%d..%d)\n",
2846         si.nPage, si.nMin, si.nMax);
2847
2848   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2849      EM_SCROLL will make visible any forcefully invisible scrollbar */
2850   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2851   memset(&si, 0, sizeof(si));
2852   si.cbSize = sizeof(si);
2853   si.fMask = SIF_PAGE | SIF_RANGE;
2854   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856     "Vertical scrollbar is invisible, should be visible.\n");
2857   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858         "reported page/range is %d (%d..%d)\n",
2859         si.nPage, si.nMin, si.nMax);
2860
2861   ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2862   memset(&si, 0, sizeof(si));
2863   si.cbSize = sizeof(si);
2864   si.fMask = SIF_PAGE | SIF_RANGE;
2865   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2866   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2867     "Vertical scrollbar is visible, should be invisible.\n");
2868   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2869         "reported page/range is %d (%d..%d)\n",
2870         si.nPage, si.nMin, si.nMax);
2871
2872   /* Again, EM_SCROLL, with SB_LINEUP */
2873   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2874   memset(&si, 0, sizeof(si));
2875   si.cbSize = sizeof(si);
2876   si.fMask = SIF_PAGE | SIF_RANGE;
2877   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2879     "Vertical scrollbar is invisible, should be visible.\n");
2880   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881         "reported page/range is %d (%d..%d)\n",
2882         si.nPage, si.nMin, si.nMax);
2883
2884   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2885   memset(&si, 0, sizeof(si));
2886   si.cbSize = sizeof(si);
2887   si.fMask = SIF_PAGE | SIF_RANGE;
2888   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890     "Vertical scrollbar is visible, should be invisible.\n");
2891   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2893         si.nPage, si.nMin, si.nMax);
2894
2895   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2896   memset(&si, 0, sizeof(si));
2897   si.cbSize = sizeof(si);
2898   si.fMask = SIF_PAGE | SIF_RANGE;
2899   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2901     "Vertical scrollbar is invisible, should be visible.\n");
2902   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903         "reported page/range is %d (%d..%d)\n",
2904         si.nPage, si.nMin, si.nMax);
2905
2906   DestroyWindow(hwndRichEdit);
2907
2908
2909   /* Test behavior with explicit visibility request, using SetWindowLong()() */
2910   hwndRichEdit = new_richedit(NULL);
2911
2912 #define ENABLE_WS_VSCROLL(hwnd) \
2913     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2914 #define DISABLE_WS_VSCROLL(hwnd) \
2915     SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2916
2917   /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2918   ENABLE_WS_VSCROLL(hwndRichEdit);
2919   memset(&si, 0, sizeof(si));
2920   si.cbSize = sizeof(si);
2921   si.fMask = SIF_PAGE | SIF_RANGE;
2922   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2924     "Vertical scrollbar is invisible, should be visible.\n");
2925   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2926         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2927         si.nPage, si.nMin, si.nMax);
2928
2929   /* Ditto, see above */
2930   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2931   memset(&si, 0, sizeof(si));
2932   si.cbSize = sizeof(si);
2933   si.fMask = SIF_PAGE | SIF_RANGE;
2934   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936     "Vertical scrollbar is invisible, should be visible.\n");
2937   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939         si.nPage, si.nMin, si.nMax);
2940
2941   /* Ditto, see above */
2942   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2943   memset(&si, 0, sizeof(si));
2944   si.cbSize = sizeof(si);
2945   si.fMask = SIF_PAGE | SIF_RANGE;
2946   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948     "Vertical scrollbar is invisible, should be visible.\n");
2949   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951         si.nPage, si.nMin, si.nMax);
2952
2953   /* Ditto, see above */
2954   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2955   memset(&si, 0, sizeof(si));
2956   si.cbSize = sizeof(si);
2957   si.fMask = SIF_PAGE | SIF_RANGE;
2958   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960     "Vertical scrollbar is invisible, should be visible.\n");
2961   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963         si.nPage, si.nMin, si.nMax);
2964
2965   /* Ditto, see above */
2966   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967   memset(&si, 0, sizeof(si));
2968   si.cbSize = sizeof(si);
2969   si.fMask = SIF_PAGE | SIF_RANGE;
2970   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972     "Vertical scrollbar is invisible, should be visible.\n");
2973   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975         si.nPage, si.nMin, si.nMax);
2976
2977   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2978   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979   memset(&si, 0, sizeof(si));
2980   si.cbSize = sizeof(si);
2981   si.fMask = SIF_PAGE | SIF_RANGE;
2982   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2984     "Vertical scrollbar is visible, should be invisible.\n");
2985   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2987         si.nPage, si.nMin, si.nMax);
2988
2989   DestroyWindow(hwndRichEdit);
2990
2991   hwndRichEdit = new_richedit(NULL);
2992
2993   DISABLE_WS_VSCROLL(hwndRichEdit);
2994   memset(&si, 0, sizeof(si));
2995   si.cbSize = sizeof(si);
2996   si.fMask = SIF_PAGE | SIF_RANGE;
2997   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2998   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2999     "Vertical scrollbar is visible, should be invisible.\n");
3000   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3001         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3002         si.nPage, si.nMin, si.nMax);
3003
3004   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3005   memset(&si, 0, sizeof(si));
3006   si.cbSize = sizeof(si);
3007   si.fMask = SIF_PAGE | SIF_RANGE;
3008   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3009   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3010     "Vertical scrollbar is visible, should be invisible.\n");
3011   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3012         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3013         si.nPage, si.nMin, si.nMax);
3014
3015   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3016   memset(&si, 0, sizeof(si));
3017   si.cbSize = sizeof(si);
3018   si.fMask = SIF_PAGE | SIF_RANGE;
3019   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3020   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3021     "Vertical scrollbar is visible, should be invisible.\n");
3022   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3023         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3024         si.nPage, si.nMin, si.nMax);
3025
3026   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3027   memset(&si, 0, sizeof(si));
3028   si.cbSize = sizeof(si);
3029   si.fMask = SIF_PAGE | SIF_RANGE;
3030   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3032     "Vertical scrollbar is visible, should be invisible.\n");
3033   ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034         "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035         si.nPage, si.nMin, si.nMax);
3036
3037   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3038   memset(&si, 0, sizeof(si));
3039   si.cbSize = sizeof(si);
3040   si.fMask = SIF_PAGE | SIF_RANGE;
3041   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3042   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3043     "Vertical scrollbar is invisible, should be visible.\n");
3044   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3045         "reported page/range is %d (%d..%d)\n",
3046         si.nPage, si.nMin, si.nMax);
3047
3048   /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3049   DISABLE_WS_VSCROLL(hwndRichEdit);
3050   memset(&si, 0, sizeof(si));
3051   si.cbSize = sizeof(si);
3052   si.fMask = SIF_PAGE | SIF_RANGE;
3053   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055     "Vertical scrollbar is visible, should be invisible.\n");
3056   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057         "reported page/range is %d (%d..%d)\n",
3058         si.nPage, si.nMin, si.nMax);
3059
3060   SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3061   memset(&si, 0, sizeof(si));
3062   si.cbSize = sizeof(si);
3063   si.fMask = SIF_PAGE | SIF_RANGE;
3064   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066     "Vertical scrollbar is visible, should be invisible.\n");
3067   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3068         "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3069         si.nPage, si.nMin, si.nMax);
3070
3071   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3072   memset(&si, 0, sizeof(si));
3073   si.cbSize = sizeof(si);
3074   si.fMask = SIF_PAGE | SIF_RANGE;
3075   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3077     "Vertical scrollbar is invisible, should be visible.\n");
3078   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3079         "reported page/range is %d (%d..%d)\n",
3080         si.nPage, si.nMin, si.nMax);
3081
3082   DISABLE_WS_VSCROLL(hwndRichEdit);
3083   memset(&si, 0, sizeof(si));
3084   si.cbSize = sizeof(si);
3085   si.fMask = SIF_PAGE | SIF_RANGE;
3086   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088     "Vertical scrollbar is visible, should be invisible.\n");
3089   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3090         "reported page/range is %d (%d..%d)\n",
3091         si.nPage, si.nMin, si.nMax);
3092
3093   /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3094      EM_SCROLL will make visible any forcefully invisible scrollbar */
3095   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3096   memset(&si, 0, sizeof(si));
3097   si.cbSize = sizeof(si);
3098   si.fMask = SIF_PAGE | SIF_RANGE;
3099   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3100   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3101     "Vertical scrollbar is invisible, should be visible.\n");
3102   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3103         "reported page/range is %d (%d..%d)\n",
3104         si.nPage, si.nMin, si.nMax);
3105
3106   DISABLE_WS_VSCROLL(hwndRichEdit);
3107   memset(&si, 0, sizeof(si));
3108   si.cbSize = sizeof(si);
3109   si.fMask = SIF_PAGE | SIF_RANGE;
3110   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3112     "Vertical scrollbar is visible, should be invisible.\n");
3113   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3114         "reported page/range is %d (%d..%d)\n",
3115         si.nPage, si.nMin, si.nMax);
3116
3117   /* Again, EM_SCROLL, with SB_LINEUP */
3118   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3119   memset(&si, 0, sizeof(si));
3120   si.cbSize = sizeof(si);
3121   si.fMask = SIF_PAGE | SIF_RANGE;
3122   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124     "Vertical scrollbar is invisible, should be visible.\n");
3125   ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126         "reported page/range is %d (%d..%d)\n",
3127         si.nPage, si.nMin, si.nMax);
3128
3129   DestroyWindow(hwndRichEdit);
3130
3131   /* This window proc models what is going on with Corman Lisp 3.0.
3132      At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3133      force the scrollbar into visibility. Recursion should NOT happen
3134      as a result of this action.
3135    */
3136   r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3137   if (r) {
3138     richeditProc = cls.lpfnWndProc;
3139     cls.lpfnWndProc = RicheditStupidOverrideProcA;
3140     cls.lpszClassName = "RicheditStupidOverride";
3141     if(!RegisterClassA(&cls)) assert(0);
3142
3143     recursionLevel = 0;
3144     WM_SIZE_recursionLevel = 0;
3145     bailedOutOfRecursion = FALSE;
3146     hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3147     ok(!bailedOutOfRecursion,
3148         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3149
3150     recursionLevel = 0;
3151     WM_SIZE_recursionLevel = 0;
3152     bailedOutOfRecursion = FALSE;
3153     MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3154     ok(!bailedOutOfRecursion,
3155         "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3156
3157     /* Unblock window in order to process WM_DESTROY */
3158     recursionLevel = 0;
3159     bailedOutOfRecursion = FALSE;
3160     WM_SIZE_recursionLevel = 0;
3161     DestroyWindow(hwndRichEdit);
3162   }
3163 }
3164
3165 static void test_EM_SETUNDOLIMIT(void)
3166 {
3167   /* cases we test for:
3168    * default behaviour - limiting at 100 undo's 
3169    * undo disabled - setting a limit of 0
3170    * undo limited -  undo limit set to some to some number, like 2
3171    * bad input - sending a negative number should default to 100 undo's */
3172  
3173   HWND hwndRichEdit = new_richedit(NULL);
3174   CHARRANGE cr;
3175   int i;
3176   int result;
3177   
3178   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3179   cr.cpMin = 0;
3180   cr.cpMax = 1;
3181   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3182     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3183       also, multiple pastes don't combine like WM_CHAR would */
3184   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3185
3186   /* first case - check the default */
3187   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3188   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3189     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3190   for (i=0; i<100; i++) /* Undo 100 of them */
3191     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3192   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3193      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3194
3195   /* second case - cannot undo */
3196   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3197   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
3198   SendMessage(hwndRichEdit,
3199               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3200   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3201      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3202
3203   /* third case - set it to an arbitrary number */
3204   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
3205   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
3206   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3207   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3208   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
3209   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3210   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3211      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3212   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3213   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3214      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3215   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
3216   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3217      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3218   
3219   /* fourth case - setting negative numbers should default to 100 undos */
3220   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
3221   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3222   ok (result == 100, 
3223       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3224       
3225   DestroyWindow(hwndRichEdit);
3226 }
3227
3228 static void test_ES_PASSWORD(void)
3229 {
3230   /* This isn't hugely testable, so we're just going to run it through its paces */
3231
3232   HWND hwndRichEdit = new_richedit(NULL);
3233   WCHAR result;
3234
3235   /* First, check the default of a regular control */
3236   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3237   ok (result == 0,
3238         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3239
3240   /* Now, set it to something normal */
3241   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3242   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3243   ok (result == 120,
3244         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3245
3246   /* Now, set it to something odd */
3247   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3248   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3249   ok (result == 1234,
3250         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3251   DestroyWindow(hwndRichEdit);
3252 }
3253
3254 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3255                                          LPBYTE pbBuff,
3256                                          LONG cb,
3257                                          LONG *pcb)
3258 {
3259   char** str = (char**)dwCookie;
3260   *pcb = cb;
3261   if (*pcb > 0) {
3262     memcpy(*str, pbBuff, *pcb);
3263     *str += *pcb;
3264   }
3265   return 0;
3266 }
3267
3268 static void test_WM_SETTEXT(void)
3269 {
3270   HWND hwndRichEdit = new_richedit(NULL);
3271   const char * TestItem1 = "TestSomeText";
3272   const char * TestItem2 = "TestSomeText\r";
3273   const char * TestItem2_after = "TestSomeText\r\n";
3274   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3275   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3276   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3277   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3278   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3279   const char * TestItem5_after = "TestSomeText TestSomeText";
3280   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3281   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3282   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3283   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3284
3285   char buf[1024] = {0};
3286   LRESULT result;
3287   EDITSTREAM es;
3288   char * p;
3289
3290   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3291      any solitary \r to be converted to \r\n on return. Properly paired
3292      \r\n are not affected. It also shows that the special sequence \r\r\n
3293      gets converted to a single space.
3294    */
3295
3296 #define TEST_SETTEXT(a, b) \
3297   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3298   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3299   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3300   ok (result == lstrlen(buf), \
3301         "WM_GETTEXT returned %ld instead of expected %u\n", \
3302         result, lstrlen(buf)); \
3303   result = strcmp(b, buf); \
3304   ok(result == 0, \
3305         "WM_SETTEXT round trip: strcmp = %ld\n", result);
3306
3307   TEST_SETTEXT(TestItem1, TestItem1)
3308   TEST_SETTEXT(TestItem2, TestItem2_after)
3309   TEST_SETTEXT(TestItem3, TestItem3_after)
3310   TEST_SETTEXT(TestItem3_after, TestItem3_after)
3311   TEST_SETTEXT(TestItem4, TestItem4_after)
3312   TEST_SETTEXT(TestItem5, TestItem5_after)
3313   TEST_SETTEXT(TestItem6, TestItem6_after)
3314   TEST_SETTEXT(TestItem7, TestItem7_after)
3315
3316   /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3317   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
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_RTF), (LPARAM)&es);
3325   trace("EM_STREAMOUT produced:\n%s\n", buf);
3326   TEST_SETTEXT(buf, TestItem1)
3327
3328 #undef TEST_SETTEXT
3329   DestroyWindow(hwndRichEdit);
3330 }
3331
3332 static void test_EM_STREAMOUT(void)
3333 {
3334   HWND hwndRichEdit = new_richedit(NULL);
3335   int r;
3336   EDITSTREAM es;
3337   char buf[1024] = {0};
3338   char * p;
3339
3340   const char * TestItem1 = "TestSomeText";
3341   const char * TestItem2 = "TestSomeText\r";
3342   const char * TestItem3 = "TestSomeText\r\n";
3343
3344   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3345   p = buf;
3346   es.dwCookie = (DWORD_PTR)&p;
3347   es.dwError = 0;
3348   es.pfnCallback = test_WM_SETTEXT_esCallback;
3349   memset(buf, 0, sizeof(buf));
3350   SendMessage(hwndRichEdit, EM_STREAMOUT,
3351               (WPARAM)(SF_TEXT), (LPARAM)&es);
3352   r = strlen(buf);
3353   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3354   ok(strcmp(buf, TestItem1) == 0,
3355         "streamed text different, got %s\n", buf);
3356
3357   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3358   p = buf;
3359   es.dwCookie = (DWORD_PTR)&p;
3360   es.dwError = 0;
3361   es.pfnCallback = test_WM_SETTEXT_esCallback;
3362   memset(buf, 0, sizeof(buf));
3363   SendMessage(hwndRichEdit, EM_STREAMOUT,
3364               (WPARAM)(SF_TEXT), (LPARAM)&es);
3365   r = strlen(buf);
3366   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3367   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3368   ok(strcmp(buf, TestItem3) == 0,
3369         "streamed text different from, got %s\n", buf);
3370   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3371   p = buf;
3372   es.dwCookie = (DWORD_PTR)&p;
3373   es.dwError = 0;
3374   es.pfnCallback = test_WM_SETTEXT_esCallback;
3375   memset(buf, 0, sizeof(buf));
3376   SendMessage(hwndRichEdit, EM_STREAMOUT,
3377               (WPARAM)(SF_TEXT), (LPARAM)&es);
3378   r = strlen(buf);
3379   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3380   ok(strcmp(buf, TestItem3) == 0,
3381         "streamed text different, got %s\n", buf);
3382
3383   DestroyWindow(hwndRichEdit);
3384 }
3385
3386 static void test_EM_SETTEXTEX(void)
3387 {
3388   HWND hwndRichEdit, parent;
3389   SCROLLINFO si;
3390   int sel_start, sel_end;
3391   SETTEXTEX setText;
3392   GETTEXTEX getText;
3393   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
3394                        'S', 'o', 'm', 'e', 
3395                        'T', 'e', 'x', 't', 0}; 
3396   WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3397                           't', 'S', 'o', 'm',
3398                           'e', 'T', 'e', 'x',
3399                           't', 't', 'S', 'o',
3400                           'm', 'e', 'T', 'e',
3401                           'x', 't', 0};
3402   WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3403                            '\r','t','S','o','m','e','T','e','x','t',0};
3404   WCHAR TestItem2[] = {'T', 'e', 's', 't',
3405                        'S', 'o', 'm', 'e',
3406                        'T', 'e', 'x', 't',
3407                       '\r', 0};
3408   const char * TestItem2_after = "TestSomeText\r\n";
3409   WCHAR TestItem3[] = {'T', 'e', 's', 't',
3410                        'S', 'o', 'm', 'e',
3411                        'T', 'e', 'x', 't',
3412                       '\r','\n','\r','\n', 0};
3413   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3414                        'S', 'o', 'm', 'e',
3415                        'T', 'e', 'x', 't',
3416                        '\n','\n', 0};
3417   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3418                        'S', 'o', 'm', 'e',
3419                        'T', 'e', 'x', 't',
3420                        '\r','\r', 0};
3421   WCHAR TestItem4[] = {'T', 'e', 's', 't',
3422                        'S', 'o', 'm', 'e',
3423                        'T', 'e', 'x', 't',
3424                       '\r','\r','\n','\r',
3425                       '\n', 0};
3426   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3427                        'S', 'o', 'm', 'e',
3428                        'T', 'e', 'x', 't',
3429                        ' ','\r', 0};
3430 #define MAX_BUF_LEN 1024
3431   WCHAR buf[MAX_BUF_LEN];
3432   char bufACP[MAX_BUF_LEN];
3433   char * p;
3434   int result;
3435   CHARRANGE cr;
3436   EDITSTREAM es;
3437   WNDCLASSA cls;
3438
3439   /* Test the scroll position with and without a parent window.
3440    *
3441    * For some reason the scroll position is 0 after EM_SETTEXTEX
3442    * with the ST_SELECTION flag only when the control has a parent
3443    * window, even though the selection is at the end. */
3444   cls.style = 0;
3445   cls.lpfnWndProc = DefWindowProcA;
3446   cls.cbClsExtra = 0;
3447   cls.cbWndExtra = 0;
3448   cls.hInstance = GetModuleHandleA(0);
3449   cls.hIcon = 0;
3450   cls.hCursor = LoadCursorA(0, IDC_ARROW);
3451   cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3452   cls.lpszMenuName = NULL;
3453   cls.lpszClassName = "ParentTestClass";
3454   if(!RegisterClassA(&cls)) assert(0);
3455
3456   parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3457                         0, 0, 200, 60, NULL, NULL, NULL, NULL);
3458   ok (parent != 0, "Failed to create parent window\n");
3459
3460   hwndRichEdit = CreateWindowEx(0,
3461                         RICHEDIT_CLASS, NULL,
3462                         ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3463                         0, 0, 200, 60, parent, NULL,
3464                         hmoduleRichEdit, NULL);
3465
3466   setText.codepage = CP_ACP;
3467   setText.flags = ST_SELECTION;
3468   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3469               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3470   si.cbSize = sizeof(si);
3471   si.fMask = SIF_ALL;
3472   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3473   todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3474   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3475   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3476   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3477
3478   DestroyWindow(parent);
3479
3480   /* Test without a parent window */
3481   hwndRichEdit = new_richedit(NULL);
3482   setText.codepage = CP_ACP;
3483   setText.flags = ST_SELECTION;
3484   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3485               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3486   si.cbSize = sizeof(si);
3487   si.fMask = SIF_ALL;
3488   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3489   ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3490   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3491   ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3492   ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3493
3494   /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3495    * but this time it is because the selection is at the beginning. */
3496   setText.codepage = CP_ACP;
3497   setText.flags = ST_DEFAULT;
3498   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3499               (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3500   si.cbSize = sizeof(si);
3501   si.fMask = SIF_ALL;
3502   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3503   ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3504   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3505   ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3506   ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3507
3508   setText.codepage = 1200;  /* no constant for unicode */
3509   getText.codepage = 1200;  /* no constant for unicode */
3510   getText.cb = MAX_BUF_LEN;
3511   getText.flags = GT_DEFAULT;
3512   getText.lpDefaultChar = NULL;
3513   getText.lpUsedDefChar = NULL;
3514
3515   setText.flags = 0;
3516   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3517   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3518   ok(lstrcmpW(buf, TestItem1) == 0,
3519       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3520
3521   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3522      convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3523    */
3524   setText.codepage = 1200;  /* no constant for unicode */
3525   getText.codepage = 1200;  /* no constant for unicode */
3526   getText.cb = MAX_BUF_LEN;
3527   getText.flags = GT_DEFAULT;
3528   getText.lpDefaultChar = NULL;
3529   getText.lpUsedDefChar = NULL;
3530   setText.flags = 0;
3531   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3532   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3533   ok(lstrcmpW(buf, TestItem2) == 0,
3534       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3535
3536   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3537   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3538   ok(strcmp((const char *)buf, TestItem2_after) == 0,
3539       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3540
3541   /* Baseline test for just-enough buffer space for string */
3542   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3543   getText.codepage = 1200;  /* no constant for unicode */
3544   getText.flags = GT_DEFAULT;
3545   getText.lpDefaultChar = NULL;
3546   getText.lpUsedDefChar = NULL;
3547   memset(buf, 0, MAX_BUF_LEN);
3548   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3549   ok(lstrcmpW(buf, TestItem2) == 0,
3550       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3551
3552   /* When there is enough space for one character, but not both, of the CRLF
3553      pair at the end of the string, the CR is not copied at all. That is,
3554      the caller must not see CRLF pairs truncated to CR at the end of the
3555      string.
3556    */
3557   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3558   getText.codepage = 1200;  /* no constant for unicode */
3559   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
3560   getText.lpDefaultChar = NULL;
3561   getText.lpUsedDefChar = NULL;
3562   memset(buf, 0, MAX_BUF_LEN);
3563   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3564   ok(lstrcmpW(buf, TestItem1) == 0,
3565       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3566
3567
3568   /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3569   setText.codepage = 1200;  /* no constant for unicode */
3570   getText.codepage = 1200;  /* no constant for unicode */
3571   getText.cb = MAX_BUF_LEN;
3572   getText.flags = GT_DEFAULT;
3573   getText.lpDefaultChar = NULL;
3574   getText.lpUsedDefChar = NULL;
3575   setText.flags = 0;
3576   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3577   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3578   ok(lstrcmpW(buf, TestItem3_after) == 0,
3579       "EM_SETTEXTEX did not convert properly\n");
3580
3581   /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3582   setText.codepage = 1200;  /* no constant for unicode */
3583   getText.codepage = 1200;  /* no constant for unicode */
3584   getText.cb = MAX_BUF_LEN;
3585   getText.flags = GT_DEFAULT;
3586   getText.lpDefaultChar = NULL;
3587   getText.lpUsedDefChar = NULL;
3588   setText.flags = 0;
3589   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3590   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3591   ok(lstrcmpW(buf, TestItem3_after) == 0,
3592       "EM_SETTEXTEX did not convert properly\n");
3593
3594   /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3595   setText.codepage = 1200;  /* no constant for unicode */
3596   getText.codepage = 1200;  /* no constant for unicode */
3597   getText.cb = MAX_BUF_LEN;
3598   getText.flags = GT_DEFAULT;
3599   getText.lpDefaultChar = NULL;
3600   getText.lpUsedDefChar = NULL;
3601   setText.flags = 0;
3602   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3603   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3604   ok(lstrcmpW(buf, TestItem4_after) == 0,
3605       "EM_SETTEXTEX did not convert properly\n");
3606
3607   /* !ST_SELECTION && Unicode && !\rtf */
3608   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3609   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3610   
3611   ok (result == 1, 
3612       "EM_SETTEXTEX returned %d, instead of 1\n",result);
3613   ok(lstrlenW(buf) == 0,
3614       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3615   
3616   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3617   setText.flags = 0;
3618   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3619   /* select some text */
3620   cr.cpMax = 1;
3621   cr.cpMin = 3;
3622   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3623   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3624   setText.flags = ST_SELECTION;
3625   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3626   ok(result == 0,
3627       "EM_SETTEXTEX with NULL lParam to replace selection"
3628       " with no text should return 0. Got %i\n",
3629       result);
3630   
3631   /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3632   setText.flags = 0;
3633   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3634   /* select some text */
3635   cr.cpMax = 1;
3636   cr.cpMin = 3;
3637   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3638   /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3639   setText.flags = ST_SELECTION;
3640   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3641                        (WPARAM)&setText, (LPARAM) TestItem1);
3642   /* get text */
3643   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3644   ok(result == lstrlenW(TestItem1),
3645       "EM_SETTEXTEX with NULL lParam to replace selection"
3646       " with no text should return 0. Got %i\n",
3647       result);
3648   ok(lstrlenW(buf) == 22,
3649       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3650       lstrlenW(buf) );
3651
3652   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3653   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3654   p = (char *)buf;
3655   es.dwCookie = (DWORD_PTR)&p;
3656   es.dwError = 0;
3657   es.pfnCallback = test_WM_SETTEXT_esCallback;
3658   memset(buf, 0, sizeof(buf));
3659   SendMessage(hwndRichEdit, EM_STREAMOUT,
3660               (WPARAM)(SF_RTF), (LPARAM)&es);
3661   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3662
3663   /* !ST_SELECTION && !Unicode && \rtf */
3664   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3665   getText.codepage = 1200;  /* no constant for unicode */
3666   getText.cb = MAX_BUF_LEN;
3667   getText.flags = GT_DEFAULT;
3668   getText.lpDefaultChar = NULL;
3669   getText.lpUsedDefChar = NULL;
3670
3671   setText.flags = 0;
3672   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3673   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3674   ok(lstrcmpW(buf, TestItem1) == 0,
3675       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3676
3677   /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3678    * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3679   setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3680   getText.codepage = CP_ACP;
3681   getText.cb = MAX_BUF_LEN;
3682   getText.flags = GT_DEFAULT;
3683   getText.lpDefaultChar = NULL;
3684   getText.lpUsedDefChar = NULL;
3685
3686   setText.flags = ST_SELECTION;
3687   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3688   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3689   todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3690   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3691   ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3692
3693   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3694   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3695   p = (char *)buf;
3696   es.dwCookie = (DWORD_PTR)&p;
3697   es.dwError = 0;
3698   es.pfnCallback = test_WM_SETTEXT_esCallback;
3699   memset(buf, 0, sizeof(buf));
3700   SendMessage(hwndRichEdit, EM_STREAMOUT,
3701               (WPARAM)(SF_RTF), (LPARAM)&es);
3702   trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3703
3704   /* select some text */
3705   cr.cpMax = 1;
3706   cr.cpMin = 3;
3707   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3708
3709   /* ST_SELECTION && !Unicode && \rtf */
3710   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3711   getText.codepage = 1200;  /* no constant for unicode */
3712   getText.cb = MAX_BUF_LEN;
3713   getText.flags = GT_DEFAULT;
3714   getText.lpDefaultChar = NULL;
3715   getText.lpUsedDefChar = NULL;
3716
3717   setText.flags = ST_SELECTION;
3718   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3719   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3720   ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3721
3722   /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3723   setText.codepage = 1200;  /* no constant for unicode */
3724   getText.codepage = CP_ACP;
3725   getText.cb = MAX_BUF_LEN;
3726
3727   setText.flags = 0;
3728   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3729   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3730
3731   /* select some text */
3732   cr.cpMax = 1;
3733   cr.cpMin = 3;
3734   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3735
3736   /* ST_SELECTION && !Unicode && !\rtf */
3737   setText.codepage = CP_ACP;
3738   getText.codepage = 1200;  /* no constant for unicode */
3739   getText.cb = MAX_BUF_LEN;
3740   getText.flags = GT_DEFAULT;
3741   getText.lpDefaultChar = NULL;
3742   getText.lpUsedDefChar = NULL;
3743
3744   setText.flags = ST_SELECTION;
3745   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3746   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3747   ok(lstrcmpW(buf, TestItem1alt) == 0,
3748       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3749       " using ST_SELECTION and non-Unicode\n");
3750
3751   /* Test setting text using rich text format */
3752   setText.flags = 0;
3753   setText.codepage = CP_ACP;
3754   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3755   getText.codepage = CP_ACP;
3756   getText.cb = MAX_BUF_LEN;
3757   getText.flags = GT_DEFAULT;
3758   getText.lpDefaultChar = NULL;
3759   getText.lpUsedDefChar = NULL;
3760   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3761   ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3762
3763   setText.flags = 0;
3764   setText.codepage = CP_ACP;
3765   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3766   getText.codepage = CP_ACP;
3767   getText.cb = MAX_BUF_LEN;
3768   getText.flags = GT_DEFAULT;
3769   getText.lpDefaultChar = NULL;
3770   getText.lpUsedDefChar = NULL;
3771   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3772   ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3773
3774   DestroyWindow(hwndRichEdit);
3775 }
3776
3777 static void test_EM_LIMITTEXT(void)
3778 {
3779   int ret;
3780
3781   HWND hwndRichEdit = new_richedit(NULL);
3782
3783   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3784    * about setting the length to -1 for multiline edit controls doesn't happen.
3785    */
3786
3787   /* Don't check default gettextlimit case. That's done in other tests */
3788
3789   /* Set textlimit to 100 */
3790   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3791   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3792   ok (ret == 100,
3793       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3794
3795   /* Set textlimit to 0 */
3796   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3797   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3798   ok (ret == 65536,
3799       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3800
3801   /* Set textlimit to -1 */
3802   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3803   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3804   ok (ret == -1,
3805       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3806
3807   /* Set textlimit to -2 */
3808   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3809   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3810   ok (ret == -2,
3811       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3812
3813   DestroyWindow (hwndRichEdit);
3814 }
3815
3816
3817 static void test_EM_EXLIMITTEXT(void)
3818 {
3819   int i, selBegin, selEnd, len1, len2;
3820   int result;
3821   char text[1024 + 1];
3822   char buffer[1024 + 1];
3823   int textlimit = 0; /* multiple of 100 */
3824   HWND hwndRichEdit = new_richedit(NULL);
3825   
3826   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3827   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3828   
3829   textlimit = 256000;
3830   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3831   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3832   /* set higher */
3833   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3834   
3835   textlimit = 1000;
3836   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3837   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3838   /* set lower */
3839   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3840  
3841   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3842   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3843   /* default for WParam = 0 */
3844   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3845  
3846   textlimit = sizeof(text)-1;
3847   memset(text, 'W', textlimit);
3848   text[sizeof(text)-1] = 0;
3849   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3850   /* maxed out text */
3851   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3852   
3853   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
3854   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3855   len1 = selEnd - selBegin;
3856   
3857   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3858   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3859   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3860   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3861   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3862   len2 = selEnd - selBegin;
3863   
3864   ok(len1 != len2,
3865     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3866     len1,len2,i);
3867   
3868   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3869   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3870   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3871   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3872   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3873   len1 = selEnd - selBegin;
3874   
3875   ok(len1 != len2,
3876     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3877     len1,len2,i);
3878   
3879   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3880   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3881   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
3882   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3883   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3884   len2 = selEnd - selBegin;
3885   
3886   ok(len1 == len2, 
3887     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3888     len1,len2,i);
3889
3890   /* set text up to the limit, select all the text, then add a char */
3891   textlimit = 5;
3892   memset(text, 'W', textlimit);
3893   text[textlimit] = 0;
3894   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3895   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3896   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3897   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3898   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3899   result = strcmp(buffer, "A");
3900   ok(0 == result, "got string = \"%s\"\n", buffer);
3901
3902   /* WM_SETTEXT not limited */
3903   textlimit = 10;
3904   memset(text, 'W', textlimit);
3905   text[textlimit] = 0;
3906   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3907   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3908   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3909   i = strlen(buffer);
3910   ok(10 == i, "expected 10 chars\n");
3911   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3912   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3913
3914   /* try inserting more text at end */
3915   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3916   ok(0 == i, "WM_CHAR wasn't processed\n");
3917   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3918   i = strlen(buffer);
3919   ok(10 == i, "expected 10 chars, got %i\n", i);
3920   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3921   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3922
3923   /* try inserting text at beginning */
3924   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3925   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3926   ok(0 == i, "WM_CHAR wasn't processed\n");
3927   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3928   i = strlen(buffer);
3929   ok(10 == i, "expected 10 chars, got %i\n", i);
3930   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3931   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3932
3933   /* WM_CHAR is limited */
3934   textlimit = 1;
3935   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3936   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
3937   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3938   ok(0 == i, "WM_CHAR wasn't processed\n");
3939   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3940   ok(0 == i, "WM_CHAR wasn't processed\n");
3941   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3942   i = strlen(buffer);
3943   ok(1 == i, "expected 1 chars, got %i instead\n", i);
3944
3945   DestroyWindow(hwndRichEdit);
3946 }
3947
3948 static void test_EM_GETLIMITTEXT(void)
3949 {
3950   int i;
3951   HWND hwndRichEdit = new_richedit(NULL);
3952
3953   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3954   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3955
3956   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3957   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3958   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3959
3960   DestroyWindow(hwndRichEdit);
3961 }
3962
3963 static void test_WM_SETFONT(void)
3964 {
3965   /* There is no invalid input or error conditions for this function.
3966    * NULL wParam and lParam just fall back to their default values 
3967    * It should be noted that even if you use a gibberish name for your fonts
3968    * here, it will still work because the name is stored. They will display as
3969    * System, but will report their name to be whatever they were created as */
3970   
3971   HWND hwndRichEdit = new_richedit(NULL);
3972   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
3973     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
3974     FF_DONTCARE, "Marlett");
3975   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
3976     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
3977     FF_DONTCARE, "MS Sans Serif");
3978   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
3979     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
3980     FF_DONTCARE, "Courier");
3981   LOGFONTA sentLogFont;
3982   CHARFORMAT2A returnedCF2A;
3983   
3984   returnedCF2A.cbSize = sizeof(returnedCF2A);
3985   
3986   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3987   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3988   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
3989
3990   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3991   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3992     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3993     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3994
3995   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3996   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
3997   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3998   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3999     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4000     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4001     
4002   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4003   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4004   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4005   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4006     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4007     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4008    
4009   /* This last test is special since we send in NULL. We clear the variables
4010    * and just compare to "System" instead of the sent in font name. */
4011   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4012   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4013   returnedCF2A.cbSize = sizeof(returnedCF2A);
4014   
4015   SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4016   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
4017   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4018   ok (!strcmp("System",returnedCF2A.szFaceName),
4019     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4020   
4021   DestroyWindow(hwndRichEdit);
4022 }
4023
4024
4025 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4026                                          LPBYTE pbBuff,
4027                                          LONG cb,
4028                                          LONG *pcb)
4029 {
4030   const char** str = (const char**)dwCookie;
4031   int size = strlen(*str);
4032   if(size > 3)  /* let's make it piecemeal for fun */
4033     size = 3;
4034   *pcb = cb;
4035   if (*pcb > size) {
4036     *pcb = size;
4037   }
4038   if (*pcb > 0) {
4039     memcpy(pbBuff, *str, *pcb);
4040     *str += *pcb;
4041   }
4042   return 0;
4043 }
4044
4045 static void test_EM_GETMODIFY(void)
4046 {
4047   HWND hwndRichEdit = new_richedit(NULL);
4048   LRESULT result;
4049   SETTEXTEX setText;
4050   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
4051                        'S', 'o', 'm', 'e', 
4052                        'T', 'e', 'x', 't', 0}; 
4053   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
4054                        'S', 'o', 'm', 'e', 
4055                        'O', 't', 'h', 'e', 'r',
4056                        'T', 'e', 'x', 't', 0}; 
4057   const char* streamText = "hello world";
4058   CHARFORMAT2 cf2;
4059   PARAFORMAT2 pf2;
4060   EDITSTREAM es;
4061   
4062   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
4063     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
4064     FF_DONTCARE, "Courier");
4065   
4066   setText.codepage = 1200;  /* no constant for unicode */
4067   setText.flags = ST_KEEPUNDO;
4068   
4069
4070   /* modify flag shouldn't be set when richedit is first created */
4071   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4072   ok (result == 0, 
4073       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4074   
4075   /* setting modify flag should actually set it */
4076   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4077   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4078   ok (result != 0, 
4079       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4080   
4081   /* clearing modify flag should actually clear it */
4082   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4083   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4084   ok (result == 0, 
4085       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4086  
4087   /* setting font doesn't change modify flag */
4088   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4089   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4090   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4091   ok (result == 0,
4092       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4093
4094   /* setting text should set modify flag */
4095   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4096   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4097   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4098   ok (result != 0,
4099       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4100   
4101   /* undo previous text doesn't reset modify flag */
4102   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4103   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4104   ok (result != 0,
4105       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4106   
4107   /* set text with no flag to keep undo stack should not set modify flag */
4108   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4109   setText.flags = 0;
4110   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4111   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4112   ok (result == 0,
4113       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4114   
4115   /* WM_SETTEXT doesn't modify */
4116   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4117   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4118   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4119   ok (result == 0,
4120       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4121   
4122   /* clear the text */
4123   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4124   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4125   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4126   ok (result == 0,
4127       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4128   
4129   /* replace text */
4130   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4131   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4132   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4133   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4134   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4135   ok (result != 0,
4136       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4137   
4138   /* copy/paste text 1 */
4139   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4140   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4141   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4142   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4143   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4144   ok (result != 0,
4145       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4146   
4147   /* copy/paste text 2 */
4148   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4149   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4150   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4151   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4152   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4153   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4154   ok (result != 0,
4155       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4156   
4157   /* press char */
4158   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4159   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4160   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4161   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4162   ok (result != 0,
4163       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4164
4165   /* press del */
4166   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4167   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4168   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4169   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4170   ok (result != 0,
4171       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4172   
4173   /* set char format */
4174   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4175   cf2.cbSize = sizeof(CHARFORMAT2);
4176   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4177              (LPARAM) &cf2);
4178   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4179   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4180   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4181   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4182   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4183   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184   ok (result != 0,
4185       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4186   
4187   /* set para format */
4188   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189   pf2.cbSize = sizeof(PARAFORMAT2);
4190   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4191              (LPARAM) &pf2);
4192   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4193   pf2.wAlignment = PFA_RIGHT;
4194   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4195   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4196   ok (result == 0,
4197       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4198
4199   /* EM_STREAM */
4200   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4201   es.dwCookie = (DWORD_PTR)&streamText;
4202   es.dwError = 0;
4203   es.pfnCallback = test_EM_GETMODIFY_esCallback;
4204   SendMessage(hwndRichEdit, EM_STREAMIN, 
4205               (WPARAM)(SF_TEXT), (LPARAM)&es);
4206   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4207   ok (result != 0,
4208       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4209
4210   DestroyWindow(hwndRichEdit);
4211 }
4212
4213 struct exsetsel_s {
4214   long min;
4215   long max;
4216   long expected_retval;
4217   int expected_getsel_start;
4218   int expected_getsel_end;
4219   int _getsel_todo_wine;
4220 };
4221
4222 const struct exsetsel_s exsetsel_tests[] = {
4223   /* sanity tests */
4224   {5, 10, 10, 5, 10, 0},
4225   {15, 17, 17, 15, 17, 0},
4226   /* test cpMax > strlen() */
4227   {0, 100, 18, 0, 18, 1},
4228   /* test cpMin == cpMax */
4229   {5, 5, 5, 5, 5, 0},
4230   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4231   {-1, 0, 5, 5, 5, 0},
4232   {-1, 17, 5, 5, 5, 0},
4233   {-1, 18, 5, 5, 5, 0},
4234   /* test cpMin < 0 && cpMax < 0 */
4235   {-1, -1, 17, 17, 17, 0},
4236   {-4, -5, 17, 17, 17, 0},
4237   /* test cMin >=0 && cpMax < 0 (bug 6814) */
4238   {0, -1, 18, 0, 18, 1},
4239   {17, -5, 18, 17, 18, 1},
4240   {18, -3, 17, 17, 17, 0},
4241   /* test if cpMin > cpMax */
4242   {15, 19, 18, 15, 18, 1},
4243   {19, 15, 18, 15, 18, 1}
4244 };
4245
4246 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4247     CHARRANGE cr;
4248     long result;
4249     int start, end;
4250
4251     cr.cpMin = setsel->min;
4252     cr.cpMax = setsel->max;
4253     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4254
4255     ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4256
4257     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4258
4259     if (setsel->_getsel_todo_wine) {
4260         todo_wine {
4261             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);
4262         }
4263     } else {
4264         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);
4265     }
4266 }
4267
4268 static void test_EM_EXSETSEL(void)
4269 {
4270     HWND hwndRichEdit = new_richedit(NULL);
4271     int i;
4272     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4273
4274     /* sending some text to the window */
4275     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4276     /*                                                 01234567890123456*/
4277     /*                                                          10      */
4278
4279     for (i = 0; i < num_tests; i++) {
4280         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4281     }
4282
4283     DestroyWindow(hwndRichEdit);
4284 }
4285
4286 static void test_EM_REPLACESEL(int redraw)
4287 {
4288     HWND hwndRichEdit = new_richedit(NULL);
4289     char buffer[1024] = {0};
4290     int r;
4291     GETTEXTEX getText;
4292     CHARRANGE cr;
4293
4294     /* sending some text to the window */
4295     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4296     /*                                                 01234567890123456*/
4297     /*                                                          10      */
4298
4299     /* FIXME add more tests */
4300     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4301     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4302     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4303     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4304     r = strcmp(buffer, "testing");
4305     ok(0 == r, "expected %d, got %d\n", 0, r);
4306
4307     DestroyWindow(hwndRichEdit);
4308
4309     hwndRichEdit = new_richedit(NULL);
4310
4311     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4312     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4313
4314     /* Test behavior with carriage returns and newlines */
4315     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4316     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4317     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4318     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4319     r = strcmp(buffer, "RichEdit1");
4320     ok(0 == r, "expected %d, got %d\n", 0, r);
4321     getText.cb = 1024;
4322     getText.codepage = CP_ACP;
4323     getText.flags = GT_DEFAULT;
4324     getText.lpDefaultChar = NULL;
4325     getText.lpUsedDefChar = NULL;
4326     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4327     ok(strcmp(buffer, "RichEdit1") == 0,
4328       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4329
4330     /* Test number of lines reported after EM_REPLACESEL */
4331     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4332     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4333
4334     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4335     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4336     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4337     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4338     r = strcmp(buffer, "RichEdit1\r\n");
4339     ok(0 == r, "expected %d, got %d\n", 0, r);
4340     getText.cb = 1024;
4341     getText.codepage = CP_ACP;
4342     getText.flags = GT_DEFAULT;
4343     getText.lpDefaultChar = NULL;
4344     getText.lpUsedDefChar = NULL;
4345     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4346     ok(strcmp(buffer, "RichEdit1\r") == 0,
4347       "EM_GETTEXTEX returned incorrect string\n");
4348
4349     /* Test number of lines reported after EM_REPLACESEL */
4350     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4351     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4352
4353     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4354        EM_REPLACESEL. The general rule seems to be that Win98's riched20
4355        returns the number of characters *inserted* into the control (after
4356        required conversions), but WinXP's riched20 returns the number of
4357        characters interpreted from the original lParam. Wine's builtin riched20
4358        implements the WinXP behavior.
4359      */
4360     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4361     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4362     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4363         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4364
4365     /* Test number of lines reported after EM_REPLACESEL */
4366     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4367     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4368
4369     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4370     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4371     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4372     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4373
4374     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4375     r = strcmp(buffer, "RichEdit1\r\n");
4376     ok(0 == r, "expected %d, got %d\n", 0, r);
4377     getText.cb = 1024;
4378     getText.codepage = CP_ACP;
4379     getText.flags = GT_DEFAULT;
4380     getText.lpDefaultChar = NULL;
4381     getText.lpUsedDefChar = NULL;
4382     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4383     ok(strcmp(buffer, "RichEdit1\r") == 0,
4384       "EM_GETTEXTEX returned incorrect string\n");
4385
4386     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4387     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4388     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4389     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4390
4391     /* The following tests show that richedit should handle the special \r\r\n
4392        sequence by turning it into a single space on insertion. However,
4393        EM_REPLACESEL on WinXP returns the number of characters in the original
4394        string.
4395      */
4396
4397     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4398     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4399     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4400     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4401     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4402     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4403     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4404
4405     /* Test the actual string */
4406     getText.cb = 1024;
4407     getText.codepage = CP_ACP;
4408     getText.flags = GT_DEFAULT;
4409     getText.lpDefaultChar = NULL;
4410     getText.lpUsedDefChar = NULL;
4411     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4412     ok(strcmp(buffer, "\r\r") == 0,
4413       "EM_GETTEXTEX returned incorrect string\n");
4414
4415     /* Test number of lines reported after EM_REPLACESEL */
4416     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4417     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4418
4419     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4420     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4421     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4422         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4423     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4424     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4425     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4426     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4427
4428     /* Test the actual string */
4429     getText.cb = 1024;
4430     getText.codepage = CP_ACP;
4431     getText.flags = GT_DEFAULT;
4432     getText.lpDefaultChar = NULL;
4433     getText.lpUsedDefChar = NULL;
4434     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4435     ok(strcmp(buffer, " ") == 0,
4436       "EM_GETTEXTEX returned incorrect string\n");
4437
4438     /* Test number of lines reported after EM_REPLACESEL */
4439     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4440     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4441
4442     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4443     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4444     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4445         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4446     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4447     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4448     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4449     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4450
4451     /* Test the actual string */
4452     getText.cb = 1024;
4453     getText.codepage = CP_ACP;
4454     getText.flags = GT_DEFAULT;
4455     getText.lpDefaultChar = NULL;
4456     getText.lpUsedDefChar = NULL;
4457     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4458     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4459       "EM_GETTEXTEX returned incorrect string\n");
4460
4461     /* Test number of lines reported after EM_REPLACESEL */
4462     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4463     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4464
4465     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4466     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4467     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4468         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4469     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4470     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4471     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4472     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4473
4474     /* Test the actual string */
4475     getText.cb = 1024;
4476     getText.codepage = CP_ACP;
4477     getText.flags = GT_DEFAULT;
4478     getText.lpDefaultChar = NULL;
4479     getText.lpUsedDefChar = NULL;
4480     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4481     ok(strcmp(buffer, " \r") == 0,
4482       "EM_GETTEXTEX returned incorrect string\n");
4483
4484     /* Test number of lines reported after EM_REPLACESEL */
4485     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4486     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4487
4488     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4489     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4490     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4491         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4492     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4493     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4494     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4495     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4496
4497     /* Test the actual string */
4498     getText.cb = 1024;
4499     getText.codepage = CP_ACP;
4500     getText.flags = GT_DEFAULT;
4501     getText.lpDefaultChar = NULL;
4502     getText.lpUsedDefChar = NULL;
4503     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4504     ok(strcmp(buffer, " \r\r") == 0,
4505       "EM_GETTEXTEX returned incorrect string\n");
4506
4507     /* Test number of lines reported after EM_REPLACESEL */
4508     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4509     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4510
4511     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4512     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4513     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4514         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4515     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4516     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4517     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4518     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4519
4520     /* Test the actual string */
4521     getText.cb = 1024;
4522     getText.codepage = CP_ACP;
4523     getText.flags = GT_DEFAULT;
4524     getText.lpDefaultChar = NULL;
4525     getText.lpUsedDefChar = NULL;
4526     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4527     ok(strcmp(buffer, "\rX\r\r\r") == 0,
4528       "EM_GETTEXTEX returned incorrect string\n");
4529
4530     /* Test number of lines reported after EM_REPLACESEL */
4531     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4532     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4533
4534     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4535     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4536     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4537     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4538     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4539     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4540     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4541
4542     /* Test the actual string */
4543     getText.cb = 1024;
4544     getText.codepage = CP_ACP;
4545     getText.flags = GT_DEFAULT;
4546     getText.lpDefaultChar = NULL;
4547     getText.lpUsedDefChar = NULL;
4548     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4549     ok(strcmp(buffer, "\r\r") == 0,
4550       "EM_GETTEXTEX returned incorrect string\n");
4551
4552     /* Test number of lines reported after EM_REPLACESEL */
4553     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4554     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4555
4556     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4557     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4558     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4559         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4560     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4561     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4562     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4563     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4564
4565     /* Test the actual string */
4566     getText.cb = 1024;
4567     getText.codepage = CP_ACP;
4568     getText.flags = GT_DEFAULT;
4569     getText.lpDefaultChar = NULL;
4570     getText.lpUsedDefChar = NULL;
4571     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4572     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4573       "EM_GETTEXTEX returned incorrect string\n");
4574
4575     /* Test number of lines reported after EM_REPLACESEL */
4576     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4577     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4578
4579     if (!redraw)
4580         /* This is needed to avoid interferring with keybd_event calls
4581          * on other tests that simulate keyboard events. */
4582         SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4583
4584     DestroyWindow(hwndRichEdit);
4585 }
4586
4587 static void test_WM_PASTE(void)
4588 {
4589     int result;
4590     char buffer[1024] = {0};
4591     const char* text1 = "testing paste\r";
4592     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4593     const char* text1_after = "testing paste\r\n";
4594     const char* text2 = "testing paste\r\rtesting paste";
4595     const char* text2_after = "testing paste\r\n\r\ntesting paste";
4596     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4597     HWND hwndRichEdit = new_richedit(NULL);
4598
4599     /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4600      * to test the state of the modifiers (Ctrl/Alt/Shift).
4601      *
4602      * Therefore Ctrl-<key> keystrokes need to be simulated with
4603      * keybd_event or by using SetKeyboardState to set the modifiers
4604      * and SendMessage to simulate the keystrokes.
4605      */
4606
4607     /* Sent keystrokes with keybd_event */
4608 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4609 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4610 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4611 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4612 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4613
4614     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4615     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4616
4617     SEND_CTRL_C(hwndRichEdit);   /* Copy */
4618     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4619     SEND_CTRL_V(hwndRichEdit);   /* Paste */
4620     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4621     /* Pasted text should be visible at this step */
4622     result = strcmp(text1_step1, buffer);
4623     ok(result == 0,
4624         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4625
4626     SEND_CTRL_Z(hwndRichEdit);   /* Undo */
4627     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4628     /* Text should be the same as before (except for \r -> \r\n conversion) */
4629     result = strcmp(text1_after, buffer);
4630     ok(result == 0,
4631         "test paste: strcmp = %i, text='%s'\n", result, buffer);
4632
4633     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4634     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4635     SEND_CTRL_C(hwndRichEdit);   /* Copy */
4636     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4637     SEND_CTRL_V(hwndRichEdit);   /* Paste */
4638     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4639     /* Pasted text should be visible at this step */
4640     result = strcmp(text3, buffer);
4641     ok(result == 0,
4642         "test paste: strcmp = %i\n", result);
4643     SEND_CTRL_Z(hwndRichEdit);   /* Undo */
4644     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4645     /* Text should be the same as before (except for \r -> \r\n conversion) */
4646     result = strcmp(text2_after, buffer);
4647     ok(result == 0,
4648         "test paste: strcmp = %i\n", result);
4649     SEND_CTRL_Y(hwndRichEdit);   /* Redo */
4650     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4651     /* Text should revert to post-paste state */
4652     result = strcmp(buffer,text3);
4653     ok(result == 0,
4654         "test paste: strcmp = %i\n", result);
4655
4656 #undef SEND_CTRL_C
4657 #undef SEND_CTRL_X
4658 #undef SEND_CTRL_V
4659 #undef SEND_CTRL_Z
4660 #undef SEND_CTRL_Y
4661
4662     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4663     /* Send WM_CHAR to simulates Ctrl-V */
4664     SendMessage(hwndRichEdit, WM_CHAR, 22,
4665                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4666     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4667     /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4668     result = strcmp(buffer,"");
4669     ok(result == 0,
4670         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4671
4672     /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4673      * with SetKeyboard state. */
4674
4675     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4676     /* Simulates paste (Ctrl-V) */
4677     hold_key(VK_CONTROL);
4678     SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4679                 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4680     release_key(VK_CONTROL);
4681     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4682     result = strcmp(buffer,"paste");
4683     ok(result == 0,
4684         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4685
4686     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4687     SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4688     /* Simulates copy (Ctrl-C) */
4689     hold_key(VK_CONTROL);
4690     SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4691                 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4692     release_key(VK_CONTROL);
4693     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4694     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4695     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4696     result = strcmp(buffer,"testing");
4697     ok(result == 0,
4698         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4699
4700     /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4701     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4702     /* Simulates select all (Ctrl-A) */
4703     hold_key(VK_CONTROL);
4704     SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4705                 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4706     /* Simulates select cut (Ctrl-X) */
4707     SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4708                 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4709     release_key(VK_CONTROL);
4710     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4711     result = strcmp(buffer,"");
4712     ok(result == 0,
4713         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4714     SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4715     SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4716     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4717     result = strcmp(buffer,"cut\r\n");
4718     todo_wine ok(result == 0,
4719         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4720     /* Simulates undo (Ctrl-Z) */
4721     hold_key(VK_CONTROL);
4722     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4723                 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4724     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4725     result = strcmp(buffer,"");
4726     ok(result == 0,
4727         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4728     /* Simulates redo (Ctrl-Y) */
4729     SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4730                 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4731     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4732     result = strcmp(buffer,"cut\r\n");
4733     todo_wine ok(result == 0,
4734         "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4735     release_key(VK_CONTROL);
4736
4737     DestroyWindow(hwndRichEdit);
4738 }
4739
4740 static void test_EM_FORMATRANGE(void)
4741 {
4742   int i, tpp_x, tpp_y;
4743   HDC hdc;
4744   HWND hwndRichEdit = new_richedit(NULL);
4745   static const struct {
4746     const char *string; /* The string */
4747     int first;          /* First 'pagebreak', 0 for don't care */
4748     int second;         /* Second 'pagebreak', 0 for don't care */
4749   } fmtstrings[] = {
4750     {"WINE wine", 0, 0},
4751     {"WINE wineWine", 0, 0},
4752     {"WINE\r\nwine\r\nwine", 5, 10},
4753     {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4754     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4755   };
4756
4757   hdc = GetDC(hwndRichEdit);
4758   ok(hdc != NULL, "Could not get HDC\n");
4759
4760   /* Calculate the twips per pixel */
4761   tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4762   tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4763
4764   SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4765
4766   for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4767   {
4768     FORMATRANGE fr;
4769     GETTEXTLENGTHEX gtl;
4770     SIZE stringsize;
4771     int r, len;
4772
4773     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4774
4775     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4776     gtl.codepage = CP_ACP;
4777     len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4778
4779     /* Get some size information for the string */
4780     GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4781
4782     /* Define the box to be half the width needed and a bit larger than the height.
4783      * Changes to the width means we have at least 2 pages. Changes to the height
4784      * is done so we can check the changing of fr.rc.bottom.
4785      */
4786     fr.hdc = fr.hdcTarget = hdc;
4787     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4788     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4789     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4790
4791     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4792     todo_wine {
4793     ok(r == len, "Expected %d, got %d\n", len, r);
4794     }
4795
4796     /* We know that the page can't hold the full string. See how many characters
4797      * are on the first one
4798      */
4799     fr.chrg.cpMin = 0;
4800     fr.chrg.cpMax = -1;
4801     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4802     todo_wine {
4803     ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4804     }
4805     if (fmtstrings[i].first)
4806       todo_wine {
4807       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4808       }
4809     else
4810       ok(r < len, "Expected < %d, got %d\n", len, r);
4811
4812     /* Do another page */
4813     fr.chrg.cpMin = r;
4814     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4815     if (fmtstrings[i].second)
4816       todo_wine {
4817       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4818       }
4819     else
4820       ok (r < len, "Expected < %d, got %d\n", len, r);
4821
4822     /* There is at least on more page, but we don't care */
4823
4824     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4825     todo_wine {
4826     ok(r == len, "Expected %d, got %d\n", len, r);
4827     }
4828   }
4829
4830   ReleaseDC(NULL, hdc);
4831   DestroyWindow(hwndRichEdit);
4832 }
4833
4834 static int nCallbackCount = 0;
4835
4836 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4837                                  LONG cb, LONG* pcb)
4838 {
4839   const char text[] = {'t','e','s','t'};
4840
4841   if (sizeof(text) <= cb)
4842   {
4843     if ((int)dwCookie != nCallbackCount)
4844     {
4845       *pcb = 0;
4846       return 0;
4847     }
4848
4849     memcpy (pbBuff, text, sizeof(text));
4850     *pcb = sizeof(text);
4851
4852     nCallbackCount++;
4853
4854     return 0;
4855   }
4856   else
4857     return 1; /* indicates callback failed */
4858 }
4859
4860 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4861                                          LPBYTE pbBuff,
4862                                          LONG cb,
4863                                          LONG *pcb)
4864 {
4865   const char** str = (const char**)dwCookie;
4866   int size = strlen(*str);
4867   *pcb = cb;
4868   if (*pcb > size) {
4869     *pcb = size;
4870   }
4871   if (*pcb > 0) {
4872     memcpy(pbBuff, *str, *pcb);
4873     *str += *pcb;
4874   }
4875   return 0;
4876 }
4877
4878 struct StringWithLength {
4879     int length;
4880     char *buffer;
4881 };
4882
4883 /* This callback is used to handled the null characters in a string. */
4884 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4885                                                    LPBYTE pbBuff,
4886                                                    LONG cb,
4887                                                    LONG *pcb)
4888 {
4889     struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4890     int size = str->length;
4891     *pcb = cb;
4892     if (*pcb > size) {
4893       *pcb = size;
4894     }
4895     if (*pcb > 0) {
4896       memcpy(pbBuff, str->buffer, *pcb);
4897       str->buffer += *pcb;
4898       str->length -= *pcb;
4899     }
4900     return 0;
4901 }
4902
4903 static void test_EM_STREAMIN(void)
4904 {
4905   HWND hwndRichEdit = new_richedit(NULL);
4906   LRESULT result;
4907   EDITSTREAM es;
4908   char buffer[1024] = {0};
4909
4910   const char * streamText0 = "{\\rtf1 TestSomeText}";
4911   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4912   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4913
4914   const char * streamText1 =
4915   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4916   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4917   "}\r\n";
4918
4919   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4920   const char * streamText2 =
4921     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4922     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4923     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4924     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4925     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4926     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4927     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4928
4929   const char * streamText3 = "RichEdit1";
4930
4931   struct StringWithLength cookieForStream4;
4932   const char * streamText4 =
4933       "This text just needs to be long enough to cause run to be split onto "
4934       "two separate lines and make sure the null terminating character is "
4935       "handled properly.\0";
4936   int length4 = strlen(streamText4) + 1;
4937   cookieForStream4.buffer = (char *)streamText4;
4938   cookieForStream4.length = length4;
4939
4940   /* Minimal test without \par at the end */
4941   es.dwCookie = (DWORD_PTR)&streamText0;
4942   es.dwError = 0;
4943   es.pfnCallback = test_EM_STREAMIN_esCallback;
4944   SendMessage(hwndRichEdit, EM_STREAMIN,
4945               (WPARAM)(SF_RTF), (LPARAM)&es);
4946
4947   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4948   ok (result  == 12,
4949       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4950   result = strcmp (buffer,"TestSomeText");
4951   ok (result  == 0,
4952       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4953   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4954
4955   /* Native richedit 2.0 ignores last \par */
4956   es.dwCookie = (DWORD_PTR)&streamText0a;
4957   es.dwError = 0;
4958   es.pfnCallback = test_EM_STREAMIN_esCallback;
4959   SendMessage(hwndRichEdit, EM_STREAMIN,
4960               (WPARAM)(SF_RTF), (LPARAM)&es);
4961
4962   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4963   ok (result  == 12,
4964       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4965   result = strcmp (buffer,"TestSomeText");
4966   ok (result  == 0,
4967       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4968   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4969
4970   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4971   es.dwCookie = (DWORD_PTR)&streamText0b;
4972   es.dwError = 0;
4973   es.pfnCallback = test_EM_STREAMIN_esCallback;
4974   SendMessage(hwndRichEdit, EM_STREAMIN,
4975               (WPARAM)(SF_RTF), (LPARAM)&es);
4976
4977   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4978   ok (result  == 14,
4979       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4980   result = strcmp (buffer,"TestSomeText\r\n");
4981   ok (result  == 0,
4982       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4983   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4984
4985   es.dwCookie = (DWORD_PTR)&streamText1;
4986   es.dwError = 0;
4987   es.pfnCallback = test_EM_STREAMIN_esCallback;
4988   SendMessage(hwndRichEdit, EM_STREAMIN,
4989               (WPARAM)(SF_RTF), (LPARAM)&es);
4990
4991   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4992   ok (result  == 12,
4993       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4994   result = strcmp (buffer,"TestSomeText");
4995   ok (result  == 0,
4996       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4997   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4998
4999   es.dwCookie = (DWORD_PTR)&streamText2;
5000   es.dwError = 0;
5001   SendMessage(hwndRichEdit, EM_STREAMIN,
5002               (WPARAM)(SF_RTF), (LPARAM)&es);
5003
5004   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5005   ok (result  == 0,
5006       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5007   ok (strlen(buffer)  == 0,
5008       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5009   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5010
5011   es.dwCookie = (DWORD_PTR)&streamText3;
5012   es.dwError = 0;
5013   SendMessage(hwndRichEdit, EM_STREAMIN,
5014               (WPARAM)(SF_RTF), (LPARAM)&es);
5015
5016   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5017   ok (result  == 0,
5018       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5019   ok (strlen(buffer)  == 0,
5020       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5021   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5022
5023   es.dwCookie = (DWORD_PTR)&cookieForStream4;
5024   es.dwError = 0;
5025   es.pfnCallback = test_EM_STREAMIN_esCallback2;
5026   SendMessage(hwndRichEdit, EM_STREAMIN,
5027               (WPARAM)(SF_TEXT), (LPARAM)&es);
5028
5029   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5030   ok (result  == length4,
5031       "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5032   ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5033
5034   DestroyWindow(hwndRichEdit);
5035 }
5036
5037 static void test_EM_StreamIn_Undo(void)
5038 {
5039   /* The purpose of this test is to determine when a EM_StreamIn should be
5040    * undoable. This is important because WM_PASTE currently uses StreamIn and
5041    * pasting should always be undoable but streaming isn't always.
5042    *
5043    * cases to test:
5044    * StreamIn plain text without SFF_SELECTION.
5045    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5046    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5047    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5048    * Feel free to add tests for other text modes or StreamIn things.
5049    */
5050
5051
5052   HWND hwndRichEdit = new_richedit(NULL);
5053   LRESULT result;
5054   EDITSTREAM es;
5055   char buffer[1024] = {0};
5056   const char randomtext[] = "Some text";
5057
5058   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5059
5060   /* StreamIn, no SFF_SELECTION */
5061   es.dwCookie = nCallbackCount;
5062   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5063   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5064   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5065   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
5066   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5067   result = strcmp (buffer,"test");
5068   ok (result  == 0,
5069       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5070
5071   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5072   ok (result == FALSE,
5073       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5074
5075   /* StreamIn, SFF_SELECTION, but nothing selected */
5076   es.dwCookie = nCallbackCount;
5077   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5078   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5079   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5080   SendMessage(hwndRichEdit, EM_STREAMIN,
5081               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5082   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5083   result = strcmp (buffer,"testSome text");
5084   ok (result  == 0,
5085       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5086
5087   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5088   ok (result == TRUE,
5089      "EM_STREAMIN with SFF_SELECTION but no selection set "
5090       "should create an undo\n");
5091
5092   /* StreamIn, SFF_SELECTION, with a selection */
5093   es.dwCookie = nCallbackCount;
5094   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5095   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5096   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5097   SendMessage(hwndRichEdit, EM_STREAMIN,
5098               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5099   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5100   result = strcmp (buffer,"Sometesttext");
5101   ok (result  == 0,
5102       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5103
5104   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5105   ok (result == TRUE,
5106       "EM_STREAMIN with SFF_SELECTION and selection set "
5107       "should create an undo\n");
5108
5109   DestroyWindow(hwndRichEdit);
5110 }
5111
5112 static BOOL is_em_settextex_supported(HWND hwnd)
5113 {
5114     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5115     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5116 }
5117
5118 static void test_unicode_conversions(void)
5119 {
5120     static const WCHAR tW[] = {'t',0};
5121     static const WCHAR teW[] = {'t','e',0};
5122     static const WCHAR textW[] = {'t','e','s','t',0};
5123     static const char textA[] = "test";
5124     char bufA[64];
5125     WCHAR bufW[64];
5126     HWND hwnd;
5127     int em_settextex_supported, ret;
5128
5129 #define set_textA(hwnd, wm_set_text, txt) \
5130     do { \
5131         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5132         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5133         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5134         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5135         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5136     } while(0)
5137 #define expect_textA(hwnd, wm_get_text, txt) \
5138     do { \
5139         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5140         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5141         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5142         memset(bufA, 0xAA, sizeof(bufA)); \
5143         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5144         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5145         ret = lstrcmpA(bufA, txt); \
5146         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5147     } while(0)
5148
5149 #define set_textW(hwnd, wm_set_text, txt) \
5150     do { \
5151         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5152         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5153         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5154         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5155         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5156     } while(0)
5157 #define expect_textW(hwnd, wm_get_text, txt) \
5158     do { \
5159         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5160         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5161         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5162         memset(bufW, 0xAA, sizeof(bufW)); \
5163         if (is_win9x) \
5164         { \
5165             assert(wm_get_text == EM_GETTEXTEX); \
5166             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5167             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5168         } \
5169         else \
5170         { \
5171             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5172             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5173         } \
5174         ret = lstrcmpW(bufW, txt); \
5175         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5176     } while(0)
5177 #define expect_empty(hwnd, wm_get_text) \
5178     do { \
5179         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5180         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5181         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5182         memset(bufA, 0xAA, sizeof(bufA)); \
5183         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5184         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5185         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5186     } while(0)
5187
5188     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5189                            0, 0, 200, 60, 0, 0, 0, 0);
5190     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5191
5192     ret = IsWindowUnicode(hwnd);
5193     if (is_win9x)
5194         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5195     else
5196         ok(ret, "RichEdit20W should be unicode under NT\n");
5197
5198     /* EM_SETTEXTEX is supported starting from version 3.0 */
5199     em_settextex_supported = is_em_settextex_supported(hwnd);
5200     trace("EM_SETTEXTEX is %ssupported on this platform\n",
5201           em_settextex_supported ? "" : "NOT ");
5202
5203     expect_empty(hwnd, WM_GETTEXT);
5204     expect_empty(hwnd, EM_GETTEXTEX);
5205
5206     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5207     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5208     expect_textA(hwnd, WM_GETTEXT, "t");
5209     expect_textA(hwnd, EM_GETTEXTEX, "t");
5210     expect_textW(hwnd, EM_GETTEXTEX, tW);
5211
5212     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5213     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5214     expect_textA(hwnd, WM_GETTEXT, "te");
5215     expect_textA(hwnd, EM_GETTEXTEX, "te");
5216     expect_textW(hwnd, EM_GETTEXTEX, teW);
5217
5218     set_textA(hwnd, WM_SETTEXT, NULL);
5219     expect_empty(hwnd, WM_GETTEXT);
5220     expect_empty(hwnd, EM_GETTEXTEX);
5221
5222     if (is_win9x)
5223         set_textA(hwnd, WM_SETTEXT, textW);
5224     else
5225         set_textA(hwnd, WM_SETTEXT, textA);
5226     expect_textA(hwnd, WM_GETTEXT, textA);
5227     expect_textA(hwnd, EM_GETTEXTEX, textA);
5228     expect_textW(hwnd, EM_GETTEXTEX, textW);
5229
5230     if (em_settextex_supported)
5231     {
5232         set_textA(hwnd, EM_SETTEXTEX, textA);
5233         expect_textA(hwnd, WM_GETTEXT, textA);
5234         expect_textA(hwnd, EM_GETTEXTEX, textA);
5235         expect_textW(hwnd, EM_GETTEXTEX, textW);
5236     }
5237
5238     if (!is_win9x)
5239     {
5240         set_textW(hwnd, WM_SETTEXT, textW);
5241         expect_textW(hwnd, WM_GETTEXT, textW);
5242         expect_textA(hwnd, WM_GETTEXT, textA);
5243         expect_textW(hwnd, EM_GETTEXTEX, textW);
5244         expect_textA(hwnd, EM_GETTEXTEX, textA);
5245
5246         if (em_settextex_supported)
5247         {
5248             set_textW(hwnd, EM_SETTEXTEX, textW);
5249             expect_textW(hwnd, WM_GETTEXT, textW);
5250             expect_textA(hwnd, WM_GETTEXT, textA);
5251             expect_textW(hwnd, EM_GETTEXTEX, textW);
5252             expect_textA(hwnd, EM_GETTEXTEX, textA);
5253         }
5254     }
5255     DestroyWindow(hwnd);
5256
5257     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5258                            0, 0, 200, 60, 0, 0, 0, 0);
5259     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5260
5261     ret = IsWindowUnicode(hwnd);
5262     ok(!ret, "RichEdit20A should NOT be unicode\n");
5263
5264     set_textA(hwnd, WM_SETTEXT, textA);
5265     expect_textA(hwnd, WM_GETTEXT, textA);
5266     expect_textA(hwnd, EM_GETTEXTEX, textA);
5267     expect_textW(hwnd, EM_GETTEXTEX, textW);
5268
5269     if (em_settextex_supported)
5270     {
5271         set_textA(hwnd, EM_SETTEXTEX, textA);
5272         expect_textA(hwnd, WM_GETTEXT, textA);
5273         expect_textA(hwnd, EM_GETTEXTEX, textA);
5274         expect_textW(hwnd, EM_GETTEXTEX, textW);
5275     }
5276
5277     if (!is_win9x)
5278     {
5279         set_textW(hwnd, WM_SETTEXT, textW);
5280         expect_textW(hwnd, WM_GETTEXT, textW);
5281         expect_textA(hwnd, WM_GETTEXT, textA);
5282         expect_textW(hwnd, EM_GETTEXTEX, textW);
5283         expect_textA(hwnd, EM_GETTEXTEX, textA);
5284
5285         if (em_settextex_supported)
5286         {
5287             set_textW(hwnd, EM_SETTEXTEX, textW);
5288             expect_textW(hwnd, WM_GETTEXT, textW);
5289             expect_textA(hwnd, WM_GETTEXT, textA);
5290             expect_textW(hwnd, EM_GETTEXTEX, textW);
5291             expect_textA(hwnd, EM_GETTEXTEX, textA);
5292         }
5293     }
5294     DestroyWindow(hwnd);
5295 }
5296
5297 static void test_WM_CHAR(void)
5298 {
5299     HWND hwnd;
5300     int ret;
5301     const char * char_list = "abc\rabc\r";
5302     const char * expected_content_single = "abcabc";
5303     const char * expected_content_multi = "abc\r\nabc\r\n";
5304     char buffer[64] = {0};
5305     const char * p;
5306
5307     /* single-line control must IGNORE carriage returns */
5308     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5309                            0, 0, 200, 60, 0, 0, 0, 0);
5310     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5311
5312     p = char_list;
5313     while (*p != '\0') {
5314         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5315         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5316         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5317         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5318         p++;
5319     }
5320
5321     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5322     ret = strcmp(buffer, expected_content_single);
5323     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5324
5325     DestroyWindow(hwnd);
5326
5327     /* multi-line control inserts CR normally */
5328     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5329                            0, 0, 200, 60, 0, 0, 0, 0);
5330     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5331
5332     p = char_list;
5333     while (*p != '\0') {
5334         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5335         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5336         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5337         SendMessageA(hwnd, WM_KEYUP, *p, 1);
5338         p++;
5339     }
5340
5341     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5342     ret = strcmp(buffer, expected_content_multi);
5343     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5344
5345     DestroyWindow(hwnd);
5346 }
5347
5348 static void test_EM_GETTEXTLENGTHEX(void)
5349 {
5350     HWND hwnd;
5351     GETTEXTLENGTHEX gtl;
5352     int ret;
5353     const char * base_string = "base string";
5354     const char * test_string = "a\nb\n\n\r\n";
5355     const char * test_string_after = "a";
5356     const char * test_string_2 = "a\rtest\rstring";
5357     char buffer[64] = {0};
5358
5359     /* single line */
5360     if (!is_win9x)
5361         hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5362                                0, 0, 200, 60, 0, 0, 0, 0);
5363     else
5364         hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5365                                0, 0, 200, 60, 0, 0, 0, 0);
5366     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5367
5368     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5369     gtl.codepage = CP_ACP;
5370     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5371     ok(ret == 0, "ret %d\n",ret);
5372
5373     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5374     gtl.codepage = CP_ACP;
5375     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5376     ok(ret == 0, "ret %d\n",ret);
5377
5378     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5379
5380     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5381     gtl.codepage = CP_ACP;
5382     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5383     ok(ret == strlen(base_string), "ret %d\n",ret);
5384
5385     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5386     gtl.codepage = CP_ACP;
5387     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5388     ok(ret == strlen(base_string), "ret %d\n",ret);
5389
5390     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5391
5392     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5393     gtl.codepage = CP_ACP;
5394     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5395     ok(ret == 1, "ret %d\n",ret);
5396
5397     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5398     gtl.codepage = CP_ACP;
5399     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5400     ok(ret == 1, "ret %d\n",ret);
5401
5402     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5403     ret = strcmp(buffer, test_string_after);
5404     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5405
5406     DestroyWindow(hwnd);
5407
5408     /* multi line */
5409     if (!is_win9x)
5410         hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5411                                0, 0, 200, 60, 0, 0, 0, 0);
5412     else
5413         hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5414                                0, 0, 200, 60, 0, 0, 0, 0);
5415     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5416
5417     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5418     gtl.codepage = CP_ACP;
5419     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5420     ok(ret == 0, "ret %d\n",ret);
5421
5422     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5423     gtl.codepage = CP_ACP;
5424     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5425     ok(ret == 0, "ret %d\n",ret);
5426
5427     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5428
5429     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5430     gtl.codepage = CP_ACP;
5431     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5432     ok(ret == strlen(base_string), "ret %d\n",ret);
5433
5434     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5435     gtl.codepage = CP_ACP;
5436     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5437     ok(ret == strlen(base_string), "ret %d\n",ret);
5438
5439     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5440
5441     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5442     gtl.codepage = CP_ACP;
5443     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5444     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5445
5446     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5447     gtl.codepage = CP_ACP;
5448     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5449     ok(ret == strlen(test_string_2), "ret %d\n",ret);
5450
5451     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5452
5453     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5454     gtl.codepage = CP_ACP;
5455     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5456     ok(ret == 10, "ret %d\n",ret);
5457
5458     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5459     gtl.codepage = CP_ACP;
5460     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5461     ok(ret == 6, "ret %d\n",ret);
5462
5463     DestroyWindow(hwnd);
5464 }
5465
5466
5467 /* globals that parent and child access when checking event masks & notifications */
5468 static HWND eventMaskEditHwnd = 0;
5469 static int queriedEventMask;
5470 static int watchForEventMask = 0;
5471
5472 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5473 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5474 {
5475     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5476     {
5477       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5478     }
5479     return DefWindowProcA(hwnd, message, wParam, lParam);
5480 }
5481
5482 /* test event masks in combination with WM_COMMAND */
5483 static void test_eventMask(void)
5484 {
5485     HWND parent;
5486     int ret, style;
5487     WNDCLASSA cls;
5488     const char text[] = "foo bar\n";
5489     int eventMask;
5490
5491     /* register class to capture WM_COMMAND */
5492     cls.style = 0;
5493     cls.lpfnWndProc = ParentMsgCheckProcA;
5494     cls.cbClsExtra = 0;
5495     cls.cbWndExtra = 0;
5496     cls.hInstance = GetModuleHandleA(0);
5497     cls.hIcon = 0;
5498     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5499     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5500     cls.lpszMenuName = NULL;
5501     cls.lpszClassName = "EventMaskParentClass";
5502     if(!RegisterClassA(&cls)) assert(0);
5503
5504     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5505                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5506     ok (parent != 0, "Failed to create parent window\n");
5507
5508     eventMaskEditHwnd = new_richedit(parent);
5509     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5510
5511     eventMask = ENM_CHANGE | ENM_UPDATE;
5512     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5513     ok(ret == ENM_NONE, "wrong event mask\n");
5514     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5515     ok(ret == eventMask, "failed to set event mask\n");
5516
5517     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5518     queriedEventMask = 0;  /* initialize to something other than we expect */
5519     watchForEventMask = EN_CHANGE;
5520     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5521     ok(ret == TRUE, "failed to set text\n");
5522     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5523        notification in response to WM_SETTEXT */
5524     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5525             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5526
5527     /* check to see if EN_CHANGE is sent when redraw is turned off */
5528     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5529     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5530     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5531     /* redraw is disabled by making the window invisible. */
5532     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5533     queriedEventMask = 0;  /* initialize to something other than we expect */
5534     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5535     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5536             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5537     SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5538     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5539
5540     /* check to see if EN_UPDATE is sent when the editor isn't visible */
5541     SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5542     style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5543     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5544     ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5545     watchForEventMask = EN_UPDATE;
5546     queriedEventMask = 0;  /* initialize to something other than we expect */
5547     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5548     ok(queriedEventMask == 0,
5549             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5550     SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5551     ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5552     queriedEventMask = 0;  /* initialize to something other than we expect */
5553     SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5554     ok(queriedEventMask == eventMask,
5555             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5556
5557
5558     DestroyWindow(parent);
5559 }
5560
5561 static int received_WM_NOTIFY = 0;
5562 static int modify_at_WM_NOTIFY = 0;
5563 static HWND hwndRichedit_WM_NOTIFY;
5564
5565 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5566 {
5567     if(message == WM_NOTIFY)
5568     {
5569       received_WM_NOTIFY = 1;
5570       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5571     }
5572     return DefWindowProcA(hwnd, message, wParam, lParam);
5573 }
5574
5575 static void test_WM_NOTIFY(void)
5576 {
5577     HWND parent;
5578     WNDCLASSA cls;
5579     CHARFORMAT2 cf2;
5580
5581     /* register class to capture WM_NOTIFY */
5582     cls.style = 0;
5583     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5584     cls.cbClsExtra = 0;
5585     cls.cbWndExtra = 0;
5586     cls.hInstance = GetModuleHandleA(0);
5587     cls.hIcon = 0;
5588     cls.hCursor = LoadCursorA(0, IDC_ARROW);
5589     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5590     cls.lpszMenuName = NULL;
5591     cls.lpszClassName = "WM_NOTIFY_ParentClass";
5592     if(!RegisterClassA(&cls)) assert(0);
5593
5594     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5595                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
5596     ok (parent != 0, "Failed to create parent window\n");
5597
5598     hwndRichedit_WM_NOTIFY = new_richedit(parent);
5599     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5600
5601     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5602
5603     /* Notifications for selection change should only be sent when selection
5604        actually changes. EM_SETCHARFORMAT is one message that calls
5605        ME_CommitUndo, which should check whether message should be sent */
5606     received_WM_NOTIFY = 0;
5607     cf2.cbSize = sizeof(CHARFORMAT2);
5608     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5609              (LPARAM) &cf2);
5610     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5611     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5612     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5613     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5614
5615     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5616        already at 0. */
5617     received_WM_NOTIFY = 0;
5618     modify_at_WM_NOTIFY = 0;
5619     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5620     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5621     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5622
5623     received_WM_NOTIFY = 0;
5624     modify_at_WM_NOTIFY = 0;
5625     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5626     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5627
5628     received_WM_NOTIFY = 0;
5629     modify_at_WM_NOTIFY = 0;
5630     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5631     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5632     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5633
5634     /* Test for WM_NOTIFY messages with redraw disabled. */
5635     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5636     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5637     received_WM_NOTIFY = 0;
5638     SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5639     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5640     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5641
5642     DestroyWindow(hwndRichedit_WM_NOTIFY);
5643     DestroyWindow(parent);
5644 }
5645
5646 static void test_undo_coalescing(void)
5647 {
5648     HWND hwnd;
5649     int result;
5650     char buffer[64] = {0};
5651
5652     /* multi-line control inserts CR normally */
5653     if (!is_win9x)
5654         hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5655                                0, 0, 200, 60, 0, 0, 0, 0);
5656     else
5657         hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5658                                0, 0, 200, 60, 0, 0, 0, 0);
5659     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5660
5661     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5662     ok (result == FALSE, "Can undo after window creation.\n");
5663     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5664     ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5665     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5666     ok (result == FALSE, "Can redo after window creation.\n");
5667     result = SendMessage(hwnd, EM_REDO, 0, 0);
5668     ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5669
5670     /* Test the effect of arrows keys during typing on undo transactions*/
5671     simulate_typing_characters(hwnd, "one two three");
5672     SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5673     SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5674     simulate_typing_characters(hwnd, " four five six");
5675
5676     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5677     ok (result == FALSE, "Can redo before anything is undone.\n");
5678     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5679     ok (result == TRUE, "Cannot undo typed characters.\n");
5680     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5681     ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5682     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5683     ok (result == TRUE, "Cannot redo after undo.\n");
5684     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5685     result = strcmp(buffer, "one two three");
5686     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5687
5688     result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5689     ok (result == TRUE, "Cannot undo typed characters.\n");
5690     result = SendMessage(hwnd, WM_UNDO, 0, 0);
5691     ok (result == TRUE, "Failed to undo typed characters.\n");
5692     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5693     result = strcmp(buffer, "");
5694     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5695
5696     /* Test the effect of focus changes during typing on undo transactions*/
5697     simulate_typing_characters(hwnd, "one two three");
5698     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5699     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5700     SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5701     SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5702     simulate_typing_characters(hwnd, " four five six");
5703     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5704     ok (result == TRUE, "Failed to undo typed characters.\n");
5705     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5706     result = strcmp(buffer, "one two three");
5707     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5708
5709     /* Test the effect of the back key during typing on undo transactions */
5710     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5711     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5712     ok (result == TRUE, "Failed to clear the text.\n");
5713     simulate_typing_characters(hwnd, "one two threa");
5714     result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5715     ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5716     SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5717     SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5718     simulate_typing_characters(hwnd, "e four five six");
5719     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5720     ok (result == TRUE, "Failed to undo typed characters.\n");
5721     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5722     result = strcmp(buffer, "");
5723     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5724
5725     /* Test the effect of the delete key during typing on undo transactions */
5726     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5727     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5728     ok(result == TRUE, "Failed to set the text.\n");
5729     SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5730     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5731     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5732     SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5733     SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5734     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5735     ok (result == TRUE, "Failed to undo typed characters.\n");
5736     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5737     result = strcmp(buffer, "acd");
5738     ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5739     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5740     ok (result == TRUE, "Failed to undo typed characters.\n");
5741     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5742     result = strcmp(buffer, "abcd");
5743     ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5744
5745     /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5746     SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5747     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5748     ok (result == TRUE, "Failed to clear the text.\n");
5749     simulate_typing_characters(hwnd, "one two three");
5750     result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5751     ok (result == 0, "expected %d but got %d\n", 0, result);
5752     simulate_typing_characters(hwnd, " four five six");
5753     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5754     ok (result == TRUE, "Failed to undo typed characters.\n");
5755     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5756     result = strcmp(buffer, "one two three");
5757     ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5758     result = SendMessage(hwnd, EM_UNDO, 0, 0);
5759     ok (result == TRUE, "Failed to undo typed characters.\n");
5760     ok (result == TRUE, "Failed to undo typed characters.\n");
5761     SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5762     result = strcmp(buffer, "");
5763     ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5764
5765     DestroyWindow(hwnd);
5766 }
5767
5768 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5769 {
5770     int length;
5771
5772     /* MSDN lied, length is actually the number of bytes. */
5773     length = bytes / sizeof(WCHAR);
5774     switch(code)
5775     {
5776         case WB_ISDELIMITER:
5777             return text[pos] == 'X';
5778         case WB_LEFT:
5779         case WB_MOVEWORDLEFT:
5780             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5781                 return pos-1;
5782             return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5783         case WB_LEFTBREAK:
5784             pos--;
5785             while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5786                 pos--;
5787             return pos;
5788         case WB_RIGHT:
5789         case WB_MOVEWORDRIGHT:
5790             if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5791                 return pos+1;
5792             return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5793         case WB_RIGHTBREAK:
5794             pos++;
5795             while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5796                 pos++;
5797             return pos;
5798         default:
5799             ok(FALSE, "Unexpected code %d\n", code);
5800             break;
5801     }
5802     return 0;
5803 }
5804
5805 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5806 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5807
5808 static void test_word_movement(void)
5809 {
5810     HWND hwnd;
5811     int result;
5812     int sel_start, sel_end;
5813     const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5814
5815     /* multi-line control inserts CR normally */
5816     hwnd = new_richedit(NULL);
5817
5818     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
5819     ok (result == TRUE, "Failed to clear the text.\n");
5820     SendMessage(hwnd, EM_SETSEL, 0, 0);
5821     /* |one two three */
5822
5823     SEND_CTRL_RIGHT(hwnd);
5824     /* one |two  three */
5825     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5826     ok(sel_start == sel_end, "Selection should be empty\n");
5827     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5828
5829     SEND_CTRL_RIGHT(hwnd);
5830     /* one two  |three */
5831     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5832     ok(sel_start == sel_end, "Selection should be empty\n");
5833     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5834
5835     SEND_CTRL_LEFT(hwnd);
5836     /* one |two  three */
5837     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5838     ok(sel_start == sel_end, "Selection should be empty\n");
5839     ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5840
5841     SEND_CTRL_LEFT(hwnd);
5842     /* |one two  three */
5843     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5844     ok(sel_start == sel_end, "Selection should be empty\n");
5845     ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5846
5847     SendMessage(hwnd, EM_SETSEL, 8, 8);
5848     /* one two | three */
5849     SEND_CTRL_RIGHT(hwnd);
5850     /* one two  |three */
5851     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5852     ok(sel_start == sel_end, "Selection should be empty\n");
5853     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5854
5855     SendMessage(hwnd, EM_SETSEL, 11, 11);
5856     /* one two  th|ree */
5857     SEND_CTRL_LEFT(hwnd);
5858     /* one two  |three */
5859     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5860     ok(sel_start == sel_end, "Selection should be empty\n");
5861     ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5862
5863     /* Test with a custom word break procedure that uses X as the delimiter. */
5864     result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5865     ok (result == TRUE, "Failed to clear the text.\n");
5866     SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5867     /* |one twoXthree */
5868     SEND_CTRL_RIGHT(hwnd);
5869     /* one twoX|three */
5870     SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5871     ok(sel_start == sel_end, "Selection should be empty\n");
5872     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5873
5874     DestroyWindow(hwnd);
5875
5876     /* Make sure the behaviour is the same with a unicode richedit window,
5877      * and using unicode functions. */
5878     if (is_win9x)
5879     {
5880         skip("Cannot test with unicode richedit window\n");
5881         return;
5882     }
5883
5884     hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5885                         ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5886                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5887
5888     /* Test with a custom word break procedure that uses X as the delimiter. */
5889     result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5890     ok (result == TRUE, "Failed to clear the text.\n");
5891     SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5892     /* |one twoXthree */
5893     SEND_CTRL_RIGHT(hwnd);
5894     /* one twoX|three */
5895     SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5896     ok(sel_start == sel_end, "Selection should be empty\n");
5897     ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5898
5899     DestroyWindow(hwnd);
5900 }
5901
5902 static void test_EM_CHARFROMPOS(void)
5903 {
5904     HWND hwnd;
5905     int result;
5906     RECT rcClient;
5907     POINTL point;
5908     point.x = 0;
5909     point.y = 40;
5910
5911     /* multi-line control inserts CR normally */
5912     hwnd = new_richedit(NULL);
5913     result = SendMessageA(hwnd, WM_SETTEXT, 0,
5914                           (LPARAM)"one two three four five six seven\reight");
5915
5916     GetClientRect(hwnd, &rcClient);
5917
5918     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5919     ok(result == 34, "expected character index of 34 but got %d\n", result);
5920
5921     /* Test with points outside the bounds of the richedit control. */
5922     point.x = -1;
5923     point.y = 40;
5924     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5925     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5926
5927     point.x = 1000;
5928     point.y = 0;
5929     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5930     todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5931
5932     point.x = 1000;
5933     point.y = 40;
5934     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5935     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5936
5937     point.x = 1000;
5938     point.y = -1;
5939     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5940     todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5941
5942     point.x = 1000;
5943     point.y = rcClient.bottom + 1;
5944     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5945     todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5946
5947     point.x = 1000;
5948     point.y = rcClient.bottom;
5949     result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5950     todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5951
5952     DestroyWindow(hwnd);
5953 }
5954
5955 static void test_word_wrap(void)
5956 {
5957     HWND hwnd;
5958     POINTL point = {0, 60}; /* This point must be below the first line */
5959     const char *text = "Must be long enough to test line wrapping";
5960     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5961     int res, pos, lines;
5962
5963     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5964      * when specified on window creation and set later. */
5965     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5966                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5967     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5968     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5969     ok(res, "WM_SETTEXT failed.\n");
5970     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5971     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5972     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5973     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5974
5975     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5976     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5977     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5978     DestroyWindow(hwnd);
5979
5980     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5981                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5982     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5983
5984     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5985     ok(res, "WM_SETTEXT failed.\n");
5986     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5987     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5988     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5989     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5990
5991     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5992     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5993     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5994     DestroyWindow(hwnd);
5995
5996     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5997                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5998     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5999     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6000     ok(res, "WM_SETTEXT failed.\n");
6001     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6002     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6003
6004     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6005     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6006     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6007     DestroyWindow(hwnd);
6008
6009     hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6010                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6011                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6012     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6013     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6014     ok(res, "WM_SETTEXT failed.\n");
6015     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6016     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6017
6018     SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6019     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6020     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6021
6022     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6023     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6024     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6025     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6026     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6027
6028     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6029     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6030     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6031     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6032     DestroyWindow(hwnd);
6033
6034     /* Test to see if wrapping happens with redraw disabled. */
6035     hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6036                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6037     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6038     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6039     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6040     ok(res, "EM_REPLACESEL failed.\n");
6041     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6042     ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6043     MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6044     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6045     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6046
6047     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6048     DestroyWindow(hwnd);
6049 }
6050
6051 static void test_autoscroll(void)
6052 {
6053     HWND hwnd = new_richedit(NULL);
6054     int lines, ret, redraw;
6055     POINT pt;
6056
6057     for (redraw = 0; redraw <= 1; redraw++) {
6058         trace("testing with WM_SETREDRAW=%d\n", redraw);
6059         SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6060         SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6061         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6062         ok(lines == 8, "%d lines instead of 8\n", lines);
6063         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6064         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6065         ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6066         ret = GetWindowLong(hwnd, GWL_STYLE);
6067         ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6068
6069         SendMessage(hwnd, WM_SETTEXT, 0, 0);
6070         lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6071         ok(lines == 1, "%d lines instead of 1\n", lines);
6072         ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6073         ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6074         ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6075         ret = GetWindowLong(hwnd, GWL_STYLE);
6076         ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6077     }
6078
6079     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6080     DestroyWindow(hwnd);
6081
6082     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6083      * auto vertical/horizontal scrolling options. */
6084     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6085                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6086                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6087     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6088     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6089     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6090     ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6091     ret = GetWindowLong(hwnd, GWL_STYLE);
6092     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6093     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6094     DestroyWindow(hwnd);
6095
6096     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6097                           WS_POPUP|ES_MULTILINE,
6098                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6099     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6100     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6101     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6102     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6103     ret = GetWindowLong(hwnd, GWL_STYLE);
6104     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6105     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6106     DestroyWindow(hwnd);
6107 }
6108
6109
6110 static void test_format_rect(void)
6111 {
6112     HWND hwnd;
6113     RECT rc, expected, clientRect;
6114     int n;
6115     DWORD options;
6116
6117     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6118                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6119                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6120     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6121
6122     GetClientRect(hwnd, &clientRect);
6123
6124     expected = clientRect;
6125     expected.left += 1;
6126     expected.right -= 1;
6127     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6128     ok(rc.top == expected.top && rc.left == expected.left &&
6129        rc.bottom == expected.bottom && rc.right == expected.right,
6130        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6131        rc.top, rc.left, rc.bottom, rc.right,
6132        expected.top, expected.left, expected.bottom, expected.right);
6133
6134     for (n = -3; n <= 3; n++)
6135     {
6136       rc = clientRect;
6137       rc.top += n;
6138       rc.left += n;
6139       rc.bottom -= n;
6140       rc.right -= n;
6141       SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6142
6143       expected = rc;
6144       expected.top = max(0, rc.top);
6145       expected.left = max(0, rc.left);
6146       expected.bottom = min(clientRect.bottom, rc.bottom);
6147       expected.right = min(clientRect.right, rc.right);
6148       SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6149       ok(rc.top == expected.top && rc.left == expected.left &&
6150          rc.bottom == expected.bottom && rc.right == expected.right,
6151          "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6152          n, rc.top, rc.left, rc.bottom, rc.right,
6153          expected.top, expected.left, expected.bottom, expected.right);
6154     }
6155
6156     rc = clientRect;
6157     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6158     expected = clientRect;
6159     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6160     ok(rc.top == expected.top && rc.left == expected.left &&
6161        rc.bottom == expected.bottom && rc.right == expected.right,
6162        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6163        rc.top, rc.left, rc.bottom, rc.right,
6164        expected.top, expected.left, expected.bottom, expected.right);
6165
6166     /* Adding the selectionbar adds the selectionbar width to the left side. */
6167     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6168     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6169     ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6170     expected.left += 8; /* selection bar width */
6171     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6172     ok(rc.top == expected.top && rc.left == expected.left &&
6173        rc.bottom == expected.bottom && rc.right == expected.right,
6174        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6175        rc.top, rc.left, rc.bottom, rc.right,
6176        expected.top, expected.left, expected.bottom, expected.right);
6177
6178     rc = clientRect;
6179     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6180     expected = clientRect;
6181     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6182     ok(rc.top == expected.top && rc.left == expected.left &&
6183        rc.bottom == expected.bottom && rc.right == expected.right,
6184        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6185        rc.top, rc.left, rc.bottom, rc.right,
6186        expected.top, expected.left, expected.bottom, expected.right);
6187
6188     /* Removing the selectionbar subtracts the selectionbar width from the left side,
6189      * even if the left side is already 0. */
6190     SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6191     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6192     ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6193     expected.left -= 8; /* selection bar width */
6194     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6195     ok(rc.top == expected.top && rc.left == expected.left &&
6196        rc.bottom == expected.bottom && rc.right == expected.right,
6197        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6198        rc.top, rc.left, rc.bottom, rc.right,
6199        expected.top, expected.left, expected.bottom, expected.right);
6200
6201     /* Set the absolute value of the formatting rectangle. */
6202     rc = clientRect;
6203     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6204     expected = clientRect;
6205     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6206     ok(rc.top == expected.top && rc.left == expected.left &&
6207        rc.bottom == expected.bottom && rc.right == expected.right,
6208        "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6209        n, rc.top, rc.left, rc.bottom, rc.right,
6210        expected.top, expected.left, expected.bottom, expected.right);
6211
6212     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6213      * LPARAM as being a relative offset when the WPARAM value is 1, but these
6214      * tests show that this isn't true. */
6215     rc.top = 15;
6216     rc.left = 15;
6217     rc.bottom = clientRect.bottom - 15;
6218     rc.right = clientRect.right - 15;
6219     expected = rc;
6220     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6221     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6222     ok(rc.top == expected.top && rc.left == expected.left &&
6223        rc.bottom == expected.bottom && rc.right == expected.right,
6224        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6225        rc.top, rc.left, rc.bottom, rc.right,
6226        expected.top, expected.left, expected.bottom, expected.right);
6227
6228     /* For some reason it does not limit the values to the client rect with
6229      * a WPARAM value of 1. */
6230     rc.top = -15;
6231     rc.left = -15;
6232     rc.bottom = clientRect.bottom + 15;
6233     rc.right = clientRect.right + 15;
6234     expected = rc;
6235     SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6236     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6237     ok(rc.top == expected.top && rc.left == expected.left &&
6238        rc.bottom == expected.bottom && rc.right == expected.right,
6239        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6240        rc.top, rc.left, rc.bottom, rc.right,
6241        expected.top, expected.left, expected.bottom, expected.right);
6242
6243     DestroyWindow(hwnd);
6244
6245     /* The extended window style affects the formatting rectangle. */
6246     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6247                           ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6248                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6249     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6250
6251     GetClientRect(hwnd, &clientRect);
6252
6253     expected = clientRect;
6254     expected.left += 1;
6255     expected.top += 1;
6256     expected.right -= 1;
6257     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6258     ok(rc.top == expected.top && rc.left == expected.left &&
6259        rc.bottom == expected.bottom && rc.right == expected.right,
6260        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6261        rc.top, rc.left, rc.bottom, rc.right,
6262        expected.top, expected.left, expected.bottom, expected.right);
6263
6264     rc = clientRect;
6265     rc.top += 5;
6266     rc.left += 5;
6267     rc.bottom -= 5;
6268     rc.right -= 5;
6269     expected = rc;
6270     expected.top -= 1;
6271     expected.left -= 1;
6272     expected.right += 1;
6273     SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6274     SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6275     ok(rc.top == expected.top && rc.left == expected.left &&
6276        rc.bottom == expected.bottom && rc.right == expected.right,
6277        "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6278        rc.top, rc.left, rc.bottom, rc.right,
6279        expected.top, expected.left, expected.bottom, expected.right);
6280
6281     DestroyWindow(hwnd);
6282 }
6283
6284 static void test_WM_GETDLGCODE(void)
6285 {
6286     HWND hwnd;
6287     UINT res, expected;
6288     MSG msg;
6289
6290     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6291
6292     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6293                           ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6294                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6295     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6296     msg.hwnd = hwnd;
6297     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6298     expected = expected | DLGC_WANTMESSAGE;
6299     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6300        res, expected);
6301     DestroyWindow(hwnd);
6302
6303     msg.message = WM_KEYDOWN;
6304     msg.wParam = VK_RETURN;
6305     msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6306     msg.pt.x = 0;
6307     msg.pt.y = 0;
6308     msg.time = GetTickCount();
6309
6310     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6311                           ES_MULTILINE|ES_WANTRETURN|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 = expected | DLGC_WANTMESSAGE;
6317     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6318        res, expected);
6319     DestroyWindow(hwnd);
6320
6321     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6322                           ES_MULTILINE|WS_POPUP,
6323                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6324     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6325     msg.hwnd = hwnd;
6326     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6327     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6328     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6329        res, expected);
6330     DestroyWindow(hwnd);
6331
6332     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6333                           ES_WANTRETURN|WS_POPUP,
6334                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6335     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6336     msg.hwnd = hwnd;
6337     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6338     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6339     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6340        res, expected);
6341     DestroyWindow(hwnd);
6342
6343     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6344                           WS_POPUP,
6345                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6346     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6347     msg.hwnd = hwnd;
6348     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6349     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6350     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6351        res, expected);
6352     DestroyWindow(hwnd);
6353
6354     msg.wParam = VK_TAB;
6355     msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6356
6357     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6358                           ES_MULTILINE|WS_POPUP,
6359                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6360     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6361     msg.hwnd = hwnd;
6362     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6363     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6364     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6365        res, expected);
6366     DestroyWindow(hwnd);
6367
6368     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6369                           WS_POPUP,
6370                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6371     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6372     msg.hwnd = hwnd;
6373     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6374     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6375     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6376        res, expected);
6377     DestroyWindow(hwnd);
6378
6379     hold_key(VK_CONTROL);
6380
6381     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6382                           ES_MULTILINE|WS_POPUP,
6383                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6384     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6385     msg.hwnd = hwnd;
6386     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6387     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6388     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6389        res, expected);
6390     DestroyWindow(hwnd);
6391
6392     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6393                           WS_POPUP,
6394                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6395     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6396     msg.hwnd = hwnd;
6397     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6398     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6399     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6400        res, expected);
6401     DestroyWindow(hwnd);
6402
6403     release_key(VK_CONTROL);
6404
6405     msg.wParam = 'a';
6406     msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6407
6408     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6409                           ES_MULTILINE|WS_POPUP,
6410                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6411     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6412     msg.hwnd = hwnd;
6413     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6414     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6415     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6416        res, expected);
6417     DestroyWindow(hwnd);
6418
6419     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6420                           WS_POPUP,
6421                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6422     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6423     msg.hwnd = hwnd;
6424     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6425     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6426     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6427        res, expected);
6428     DestroyWindow(hwnd);
6429
6430     msg.message = WM_CHAR;
6431
6432     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6433                           ES_MULTILINE|WS_POPUP,
6434                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6435     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6436     msg.hwnd = hwnd;
6437     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6438     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6439     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6440        res, expected);
6441     DestroyWindow(hwnd);
6442
6443     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6444                           WS_POPUP,
6445                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6446     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6447     msg.hwnd = hwnd;
6448     res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6449     expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6450     ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6451        res, expected);
6452     DestroyWindow(hwnd);
6453 }
6454
6455 static void test_zoom(void)
6456 {
6457     HWND hwnd;
6458     UINT ret;
6459     RECT rc;
6460     POINT pt;
6461     int numerator, denominator;
6462
6463     hwnd = new_richedit(NULL);
6464     GetClientRect(hwnd, &rc);
6465     pt.x = (rc.right - rc.left) / 2;
6466     pt.y = (rc.bottom - rc.top) / 2;
6467     ClientToScreen(hwnd, &pt);
6468
6469     /* Test initial zoom value */
6470     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6471     ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6472     ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6473     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6474
6475     /* test scroll wheel */
6476     hold_key(VK_CONTROL);
6477     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6478                       MAKELPARAM(pt.x, pt.y));
6479     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6480     release_key(VK_CONTROL);
6481
6482     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6483     ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6484     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6485     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6486
6487     /* Test how much the mouse wheel can zoom in and out. */
6488     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6489     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6490
6491     hold_key(VK_CONTROL);
6492     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6493                       MAKELPARAM(pt.x, pt.y));
6494     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6495     release_key(VK_CONTROL);
6496
6497     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6498     ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6499     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6500     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6501
6502     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6503     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6504
6505     hold_key(VK_CONTROL);
6506     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6507                       MAKELPARAM(pt.x, pt.y));
6508     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6509     release_key(VK_CONTROL);
6510
6511     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6512     ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6513     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6514     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6515
6516     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6517     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6518
6519     hold_key(VK_CONTROL);
6520     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6521                       MAKELPARAM(pt.x, pt.y));
6522     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6523     release_key(VK_CONTROL);
6524
6525     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6526     ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6527     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6528     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6529
6530     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6531     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6532
6533     hold_key(VK_CONTROL);
6534     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6535                       MAKELPARAM(pt.x, pt.y));
6536     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6537     release_key(VK_CONTROL);
6538
6539     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6540     ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6541     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6542     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6543
6544     /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6545     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6546     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6547
6548     hold_key(VK_CONTROL);
6549     ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6550                       MAKELPARAM(pt.x, pt.y));
6551     ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6552     release_key(VK_CONTROL);
6553
6554     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6555     ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6556     ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6557     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6558
6559     /* Test bounds checking on EM_SETZOOM */
6560     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6561     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6562
6563     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6564     ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6565
6566     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6567     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6568
6569     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6570     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6571     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6572     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6573
6574     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6575     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6576
6577     /* See if negative numbers are accepted. */
6578     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6579     ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6580
6581     /* See if negative numbers are accepted. */
6582     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6583     ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6584
6585     ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6586     ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6587     ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6588     ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6589
6590     /* Reset the zoom value */
6591     ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6592     ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6593
6594     DestroyWindow(hwnd);
6595 }
6596
6597 START_TEST( editor )
6598 {
6599   /* Must explicitly LoadLibrary(). The test has no references to functions in
6600    * RICHED20.DLL, so the linker doesn't actually link to it. */
6601   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6602   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6603
6604   is_win9x = GetVersion() & 0x80000000;
6605
6606   test_WM_CHAR();
6607   test_EM_FINDTEXT();
6608   test_EM_GETLINE();
6609   test_EM_POSFROMCHAR();
6610   test_EM_SCROLLCARET();
6611   test_EM_SCROLL();
6612   test_scrollbar_visibility();
6613   test_WM_SETTEXT();
6614   test_EM_LINELENGTH();
6615   test_EM_SETCHARFORMAT();
6616   test_EM_SETTEXTMODE();
6617   test_TM_PLAINTEXT();
6618   test_EM_SETOPTIONS();
6619   test_WM_GETTEXT();
6620   test_EM_GETTEXTRANGE();
6621   test_EM_GETSELTEXT();
6622   test_EM_SETUNDOLIMIT();
6623   test_ES_PASSWORD();
6624   test_EM_SETTEXTEX();
6625   test_EM_LIMITTEXT();
6626   test_EM_EXLIMITTEXT();
6627   test_EM_GETLIMITTEXT();
6628   test_WM_SETFONT();
6629   test_EM_GETMODIFY();
6630   test_EM_EXSETSEL();
6631   test_WM_PASTE();
6632   test_EM_STREAMIN();
6633   test_EM_STREAMOUT();
6634   test_EM_StreamIn_Undo();
6635   test_EM_FORMATRANGE();
6636   test_unicode_conversions();
6637   test_EM_GETTEXTLENGTHEX();
6638   test_EM_REPLACESEL(1);
6639   test_EM_REPLACESEL(0);
6640   test_WM_NOTIFY();
6641   test_EM_AUTOURLDETECT();
6642   test_eventMask();
6643   test_undo_coalescing();
6644   test_word_movement();
6645   test_EM_CHARFROMPOS();
6646   test_SETPARAFORMAT();
6647   test_word_wrap();
6648   test_autoscroll();
6649   test_format_rect();
6650   test_WM_GETDLGCODE();
6651   test_zoom();
6652
6653   /* Set the environment variable WINETEST_RICHED20 to keep windows
6654    * responsive and open for 30 seconds. This is useful for debugging.
6655    */
6656   if (getenv( "WINETEST_RICHED20" )) {
6657     keep_responsive(30);
6658   }
6659
6660   OleFlushClipboard();
6661   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
6662 }