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