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