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