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