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