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