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