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