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