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