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