richedit: WM_SETTEXT must immediately autodetect URLs, without waiting for a WM_CHAR.
[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_SETCHARFORMAT(void)
401 {
402   HWND hwndRichEdit = new_richedit(NULL);
403   CHARFORMAT2 cf2;
404   int rc = 0;
405
406   /* Invalid flags, CHARFORMAT2 structure blanked out */
407   memset(&cf2, 0, sizeof(cf2));
408   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
409              (LPARAM) &cf2);
410   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
411
412   /* A valid flag, CHARFORMAT2 structure blanked out */
413   memset(&cf2, 0, sizeof(cf2));
414   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
415              (LPARAM) &cf2);
416   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
417
418   /* A valid flag, CHARFORMAT2 structure blanked out */
419   memset(&cf2, 0, sizeof(cf2));
420   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
421              (LPARAM) &cf2);
422   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
423
424   /* A valid flag, CHARFORMAT2 structure blanked out */
425   memset(&cf2, 0, sizeof(cf2));
426   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
427              (LPARAM) &cf2);
428   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
429
430   /* A valid flag, CHARFORMAT2 structure blanked out */
431   memset(&cf2, 0, sizeof(cf2));
432   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
433              (LPARAM) &cf2);
434   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
435
436   /* Invalid flags, CHARFORMAT2 structure minimally filled */
437   memset(&cf2, 0, sizeof(cf2));
438   cf2.cbSize = sizeof(CHARFORMAT2);
439   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
440              (LPARAM) &cf2);
441   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
442
443   /* A valid flag, CHARFORMAT2 structure minimally filled */
444   memset(&cf2, 0, sizeof(cf2));
445   cf2.cbSize = sizeof(CHARFORMAT2);
446   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
447              (LPARAM) &cf2);
448   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
449
450   /* A valid flag, CHARFORMAT2 structure minimally filled */
451   memset(&cf2, 0, sizeof(cf2));
452   cf2.cbSize = sizeof(CHARFORMAT2);
453   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
454              (LPARAM) &cf2);
455   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
456
457   /* A valid flag, CHARFORMAT2 structure minimally filled */
458   memset(&cf2, 0, sizeof(cf2));
459   cf2.cbSize = sizeof(CHARFORMAT2);
460   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
461              (LPARAM) &cf2);
462   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
463
464   /* A valid flag, CHARFORMAT2 structure minimally filled */
465   memset(&cf2, 0, sizeof(cf2));
466   cf2.cbSize = sizeof(CHARFORMAT2);
467   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
468              (LPARAM) &cf2);
469   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
470
471   cf2.cbSize = sizeof(CHARFORMAT2);
472   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
473              (LPARAM) &cf2);
474
475   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
476   cf2.cbSize = sizeof(CHARFORMAT2);
477   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
478              (LPARAM) &cf2);
479   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
480   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
481
482   /* wParam==0 is default char format, does not set modify */
483   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
484   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
485   ok(rc == 0, "Text marked as modified, expected not modified!\n");
486   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
487   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
488   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
489   ok(rc == 0, "Text marked as modified, expected not modified!\n");
490
491   /* wParam==SCF_SELECTION sets modify if nonempty selection */
492   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
493   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
494   ok(rc == 0, "Text marked as modified, expected not modified!\n");
495   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
496   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
497   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
498   ok(rc == 0, "Text marked as modified, expected not modified!\n");
499
500   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
501   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
502   ok(rc == 0, "Text marked as modified, expected not modified!\n");
503   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
504   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
505   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
506   ok(rc == 0, "Text marked as modified, expected not modified!\n");
507   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
508   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
509   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
510   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
511   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
512
513   /* wParam==SCF_ALL sets modify regardless of whether text is present */
514   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
515   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
516   ok(rc == 0, "Text marked as modified, expected not modified!\n");
517   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
518   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
519   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
520   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
521
522   DestroyWindow(hwndRichEdit);
523 }
524
525 static void test_EM_SETTEXTMODE(void)
526 {
527   HWND hwndRichEdit = new_richedit(NULL);
528   CHARFORMAT2 cf2, cf2test;
529   CHARRANGE cr;
530   int rc = 0;
531
532   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
533   /*Insert text into the control*/
534
535   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
536
537   /*Attempt to change the control to plain text mode*/
538   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
539   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
540
541   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
542   If rich text is pasted, it should have the same formatting as the rest
543   of the text in the control*/
544
545   /*Italicize the text
546   *NOTE: If the default text was already italicized, the test will simply
547   reverse; in other words, it will copy a regular "wine" into a plain
548   text window that uses an italicized format*/
549   cf2.cbSize = sizeof(CHARFORMAT2);
550   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
551              (LPARAM) &cf2);
552
553   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
554   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
555
556   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
557   ok(rc == 0, "Text marked as modified, expected not modified!\n");
558
559   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
560   however, SCF_ALL has been implemented*/
561   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
562   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
563
564   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
565   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
566
567   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
568
569   /*Select the string "wine"*/
570   cr.cpMin = 0;
571   cr.cpMax = 4;
572   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
573
574   /*Copy the italicized "wine" to the clipboard*/
575   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
576
577   /*Reset the formatting to default*/
578   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
579   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
580   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
581
582   /*Clear the text in the control*/
583   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
584
585   /*Switch to Plain Text Mode*/
586   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
587   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
588
589   /*Input "wine" again in normal format*/
590   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
591
592   /*Paste the italicized "wine" into the control*/
593   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
594
595   /*Select a character from the first "wine" string*/
596   cr.cpMin = 2;
597   cr.cpMax = 3;
598   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
599
600   /*Retrieve its formatting*/
601   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
602               (LPARAM) &cf2);
603
604   /*Select a character from the second "wine" string*/
605   cr.cpMin = 5;
606   cr.cpMax = 6;
607   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
608
609   /*Retrieve its formatting*/
610   cf2test.cbSize = sizeof(CHARFORMAT2);
611   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
612                (LPARAM) &cf2test);
613
614   /*Compare the two formattings*/
615     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
616       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
617        cf2.dwEffects, cf2test.dwEffects);
618   /*Test TM_RICHTEXT by: switching back to Rich Text mode
619                          printing "wine" in the current format(normal)
620                          pasting "wine" from the clipboard(italicized)
621                          comparing the two formats(should differ)*/
622
623   /*Attempt to switch with text in control*/
624   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
625   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
626
627   /*Clear control*/
628   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
629
630   /*Switch into Rich Text mode*/
631   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
632   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
633
634   /*Print "wine" in normal formatting into the control*/
635   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
636
637   /*Paste italicized "wine" into the control*/
638   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
639
640   /*Select text from the first "wine" string*/
641   cr.cpMin = 1;
642   cr.cpMax = 3;
643   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
644
645   /*Retrieve its formatting*/
646   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
647                 (LPARAM) &cf2);
648
649   /*Select text from the second "wine" string*/
650   cr.cpMin = 6;
651   cr.cpMax = 7;
652   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
653
654   /*Retrieve its formatting*/
655   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
656                 (LPARAM) &cf2test);
657
658   /*Test that the two formattings are not the same*/
659   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
660       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
661       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
662
663   DestroyWindow(hwndRichEdit);
664 }
665
666 static void test_TM_PLAINTEXT(void)
667 {
668   /*Tests plain text properties*/
669
670   HWND hwndRichEdit = new_richedit(NULL);
671   CHARFORMAT2 cf2, cf2test;
672   CHARRANGE cr;
673   int rc = 0;
674
675   /*Switch to plain text mode*/
676
677   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
678   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
679
680   /*Fill control with text*/
681
682   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
683
684   /*Select some text and bold it*/
685
686   cr.cpMin = 10;
687   cr.cpMax = 20;
688   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
689   cf2.cbSize = sizeof(CHARFORMAT2);
690   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
691               (LPARAM) &cf2);
692
693   cf2.dwMask = CFM_BOLD | cf2.dwMask;
694   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
695
696   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
697   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
698
699   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
700   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
701
702   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
703   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
704
705   /*Get the formatting of those characters*/
706
707   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
708
709   /*Get the formatting of some other characters*/
710   cf2test.cbSize = sizeof(CHARFORMAT2);
711   cr.cpMin = 21;
712   cr.cpMax = 30;
713   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
714   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
715
716   /*Test that they are the same as plain text allows only one formatting*/
717
718   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
719      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
720      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
721   
722   /*Fill the control with a "wine" string, which when inserted will be bold*/
723
724   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
725
726   /*Copy the bolded "wine" string*/
727
728   cr.cpMin = 0;
729   cr.cpMax = 4;
730   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
731   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
732
733   /*Swap back to rich text*/
734
735   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
736   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
737
738   /*Set the default formatting to bold italics*/
739
740   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
741   cf2.dwMask |= CFM_ITALIC;
742   cf2.dwEffects ^= CFE_ITALIC;
743   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
744   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
745
746   /*Set the text in the control to "wine", which will be bold and italicized*/
747
748   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
749
750   /*Paste the plain text "wine" string, which should take the insert
751    formatting, which at the moment is bold italics*/
752
753   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
754
755   /*Select the first "wine" string and retrieve its formatting*/
756
757   cr.cpMin = 1;
758   cr.cpMax = 3;
759   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
760   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
761
762   /*Select the second "wine" string and retrieve its formatting*/
763
764   cr.cpMin = 5;
765   cr.cpMax = 7;
766   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
767   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
768
769   /*Compare the two formattings. They should be the same.*/
770
771   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
772      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
773      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
774   DestroyWindow(hwndRichEdit);
775 }
776
777 static void test_WM_GETTEXT(void)
778 {
779     HWND hwndRichEdit = new_richedit(NULL);
780     static const char text[] = "Hello. My name is RichEdit!";
781     static const char text2[] = "Hello. My name is RichEdit!\r";
782     static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
783     char buffer[1024] = {0};
784     int result;
785
786     /* Baseline test with normal-sized buffer */
787     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
788     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
789     ok(result == lstrlen(buffer),
790         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
791     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
792     result = strcmp(buffer,text);
793     ok(result == 0, 
794         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
795
796     /* Test for returned value of WM_GETTEXTLENGTH */
797     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
798     ok(result == lstrlen(text),
799         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
800         result, lstrlen(text));
801
802     /* Test for behavior in overflow case */
803     memset(buffer, 0, 1024);
804     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
805     ok(result == 0 ||
806        result == lstrlenA(text) - 1, /* XP, win2k3 */
807         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
808     result = strcmp(buffer,text);
809     if (result)
810         result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
811     ok(result == 0,
812         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
813
814     /* Baseline test with normal-sized buffer and carriage return */
815     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
816     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
817     ok(result == lstrlen(buffer),
818         "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
819     result = strcmp(buffer,text2_after);
820     ok(result == 0,
821         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
822
823     /* Test for returned value of WM_GETTEXTLENGTH */
824     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
825     ok(result == lstrlen(text2_after),
826         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
827         result, lstrlen(text2_after));
828
829     /* Test for behavior of CRLF conversion in case of overflow */
830     memset(buffer, 0, 1024);
831     result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
832     ok(result == 0 ||
833        result == lstrlenA(text2) - 1, /* XP, win2k3 */
834         "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
835     result = strcmp(buffer,text2);
836     if (result)
837         result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
838     ok(result == 0,
839         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
840
841     DestroyWindow(hwndRichEdit);
842 }
843
844 static void test_EM_GETTEXTRANGE(void)
845 {
846     HWND hwndRichEdit = new_richedit(NULL);
847     const char * text1 = "foo bar\r\nfoo bar";
848     const char * text2 = "foo bar\rfoo bar";
849     const char * expect = "bar\rfoo";
850     char buffer[1024] = {0};
851     LRESULT result;
852     TEXTRANGEA textRange;
853
854     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
855
856     textRange.lpstrText = buffer;
857     textRange.chrg.cpMin = 4;
858     textRange.chrg.cpMax = 11;
859     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
860     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
861     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
862
863     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
864
865     textRange.lpstrText = buffer;
866     textRange.chrg.cpMin = 4;
867     textRange.chrg.cpMax = 11;
868     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
869     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
870     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
871
872     DestroyWindow(hwndRichEdit);
873 }
874
875 static void test_EM_GETSELTEXT(void)
876 {
877     HWND hwndRichEdit = new_richedit(NULL);
878     const char * text1 = "foo bar\r\nfoo bar";
879     const char * text2 = "foo bar\rfoo bar";
880     const char * expect = "bar\rfoo";
881     char buffer[1024] = {0};
882     LRESULT result;
883
884     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
885
886     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
887     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
888     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
889     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
890
891     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
892
893     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
894     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
895     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
896     ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
897
898     DestroyWindow(hwndRichEdit);
899 }
900
901 /* FIXME: need to test unimplemented options and robustly test wparam */
902 static void test_EM_SETOPTIONS(void)
903 {
904     HWND hwndRichEdit = new_richedit(NULL);
905     static const char text[] = "Hello. My name is RichEdit!";
906     char buffer[1024] = {0};
907
908     /* NEGATIVE TESTING - NO OPTIONS SET */
909     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
910     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
911
912     /* testing no readonly by sending 'a' to the control*/
913     SetFocus(hwndRichEdit);
914     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
915     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
916     ok(buffer[0]=='a', 
917        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
918     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
919
920     /* READONLY - sending 'a' to the control */
921     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
922     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
923     SetFocus(hwndRichEdit);
924     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
925     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
926     ok(buffer[0]==text[0], 
927        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
928
929     DestroyWindow(hwndRichEdit);
930 }
931
932 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
933 {
934   CHARFORMAT2W text_format;
935   text_format.cbSize = sizeof(text_format);
936   SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
937   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
938   return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
939 }
940
941 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
942 {
943   int link_present = 0;
944
945   link_present = check_CFE_LINK_selection(hwnd, 0, 1);
946   if (is_url) 
947   { /* control text is url; should get CFE_LINK */
948         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
949   }
950   else 
951   {
952     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
953   }
954 }
955
956 static HWND new_static_wnd(HWND parent) {
957   return new_window("Static", 0, parent);
958 }
959
960 static void test_EM_AUTOURLDETECT(void)
961 {
962   struct urls_s {
963     const char *text;
964     int is_url;
965   } urls[12] = {
966     {"winehq.org", 0},
967     {"http://www.winehq.org", 1},
968     {"http//winehq.org", 0},
969     {"ww.winehq.org", 0},
970     {"www.winehq.org", 1},
971     {"ftp://192.168.1.1", 1},
972     {"ftp//192.168.1.1", 0},
973     {"mailto:your@email.com", 1},    
974     {"prospero:prosperoserver", 1},
975     {"telnet:test", 1},
976     {"news:newserver", 1},
977     {"wais:waisserver", 1}  
978   };
979
980   int i, j;
981   int urlRet=-1;
982   HWND hwndRichEdit, parent;
983
984   /* All of the following should cause the URL to be detected  */
985   const char * templates_delim[] = {
986     "This is some text with X on it",
987     "This is some text with (X) on it",
988     "This is some text with X\r on it",
989     "This is some text with ---X--- on it",
990     "This is some text with \"X\" on it",
991     "This is some text with 'X' on it",
992     "This is some text with 'X' on it",
993     "This is some text with :X: on it",
994
995     "This text ends with X",
996
997     "This is some text with X) on it",
998     "This is some text with X--- on it",
999     "This is some text with X\" on it",
1000     "This is some text with X' on it",
1001     "This is some text with X: on it",
1002
1003     "This is some text with (X on it",
1004     "This is some text with \rX on it",
1005     "This is some text with ---X on it",
1006     "This is some text with \"X on it",
1007     "This is some text with 'X on it",
1008     "This is some text with :X on it",
1009   };
1010   /* None of these should cause the URL to be detected */
1011   const char * templates_non_delim[] = {
1012     "This is some text with |X| on it",
1013     "This is some text with *X* on it",
1014     "This is some text with /X/ on it",
1015     "This is some text with +X+ on it",
1016     "This is some text with %X% on it",
1017     "This is some text with #X# on it",
1018     "This is some text with @X@ on it",
1019     "This is some text with \\X\\ on it",
1020     "This is some text with |X on it",
1021     "This is some text with *X on it",
1022     "This is some text with /X on it",
1023     "This is some text with +X on it",
1024     "This is some text with %X on it",
1025     "This is some text with #X on it",
1026     "This is some text with @X on it",
1027     "This is some text with \\X on it",
1028   };
1029   /* All of these cause the URL detection to be extended by one more byte,
1030      thus demonstrating that the tested character is considered as part
1031      of the URL. */
1032   const char * templates_xten_delim[] = {
1033     "This is some text with X| on it",
1034     "This is some text with X* on it",
1035     "This is some text with X/ on it",
1036     "This is some text with X+ on it",
1037     "This is some text with X% on it",
1038     "This is some text with X# on it",
1039     "This is some text with X@ on it",
1040     "This is some text with X\\ on it",
1041   };
1042   char buffer[1024];
1043
1044   parent = new_static_wnd(NULL);
1045   hwndRichEdit = new_richedit(parent);
1046   /* Try and pass EM_AUTOURLDETECT some test wParam values */
1047   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1048   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1049   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1050   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1051   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1052   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1053   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1054   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1055   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1056   /* for each url, check the text to see if CFE_LINK effect is present */
1057   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1058
1059     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1060     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1061     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1062
1063     /* Link detection should happen immediately upon WM_SETTEXT */
1064     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1065     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1066     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1067   }
1068   DestroyWindow(hwndRichEdit);
1069
1070   /* Test detection of URLs within normal text - WM_SETTEXT case. */
1071   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1072     hwndRichEdit = new_richedit(parent);
1073
1074     for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1075       char * at_pos;
1076       int at_offset;
1077       int end_offset;
1078
1079       at_pos = strchr(templates_delim[j], 'X');
1080       at_offset = at_pos - templates_delim[j];
1081       strncpy(buffer, templates_delim[j], at_offset);
1082       buffer[at_offset] = '\0';
1083       strcat(buffer, urls[i].text);
1084       strcat(buffer, templates_delim[j] + at_offset + 1);
1085       end_offset = at_offset + strlen(urls[i].text);
1086
1087       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1088       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1089
1090       /* This assumes no templates start with the URL itself, and that they
1091          have at least two characters before the URL text */
1092       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1093         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1094       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1095         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1096       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1097         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1098
1099       if (urls[i].is_url)
1100       {
1101         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1102           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1103         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1104           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1105       }
1106       else
1107       {
1108         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1109           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1110         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1111           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1112       }
1113       if (buffer[end_offset] != '\0')
1114       {
1115         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1116           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1117         if (buffer[end_offset +1] != '\0')
1118         {
1119           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1120             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1121         }
1122       }
1123     }
1124
1125     for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1126       char * at_pos;
1127       int at_offset;
1128       int end_offset;
1129
1130       at_pos = strchr(templates_non_delim[j], 'X');
1131       at_offset = at_pos - templates_non_delim[j];
1132       strncpy(buffer, templates_non_delim[j], at_offset);
1133       buffer[at_offset] = '\0';
1134       strcat(buffer, urls[i].text);
1135       strcat(buffer, templates_non_delim[j] + at_offset + 1);
1136       end_offset = at_offset + strlen(urls[i].text);
1137
1138       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1139       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1140
1141       /* This assumes no templates start with the URL itself, and that they
1142          have at least two characters before the URL text */
1143       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1144         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1145       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1146         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1147       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1148         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1149
1150       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1151         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1152       ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1153         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1154       if (buffer[end_offset] != '\0')
1155       {
1156         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1157           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1158         if (buffer[end_offset +1] != '\0')
1159         {
1160           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1161             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1162         }
1163       }
1164     }
1165
1166     for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1167       char * at_pos;
1168       int at_offset;
1169       int end_offset;
1170
1171       at_pos = strchr(templates_xten_delim[j], 'X');
1172       at_offset = at_pos - templates_xten_delim[j];
1173       strncpy(buffer, templates_xten_delim[j], at_offset);
1174       buffer[at_offset] = '\0';
1175       strcat(buffer, urls[i].text);
1176       strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1177       end_offset = at_offset + strlen(urls[i].text);
1178
1179       SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1180       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1181
1182       /* This assumes no templates start with the URL itself, and that they
1183          have at least two characters before the URL text */
1184       ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1185         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1186       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1187         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1188       ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1189         "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1190
1191       if (urls[i].is_url)
1192       {
1193         ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1194           "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1195         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1196           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1197         ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1198           "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1199       }
1200       else
1201       {
1202         ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1203           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1204         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1205           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1206         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1207           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1208       }
1209       if (buffer[end_offset +1] != '\0')
1210       {
1211         ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1212           "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1213         if (buffer[end_offset +2] != '\0')
1214         {
1215           ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1216             "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1217         }
1218       }
1219     }
1220
1221     DestroyWindow(hwndRichEdit);
1222     hwndRichEdit = NULL;
1223   }
1224
1225   DestroyWindow(parent);
1226 }
1227
1228 static void test_EM_SCROLL(void)
1229 {
1230   int i, j;
1231   int r; /* return value */
1232   int expr; /* expected return value */
1233   HWND hwndRichEdit = new_richedit(NULL);
1234   int y_before, y_after; /* units of lines of text */
1235
1236   /* test a richedit box containing a single line of text */
1237   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
1238   expr = 0x00010000;
1239   for (i = 0; i < 4; i++) {
1240     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
1241
1242     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
1243     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1244     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
1245        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
1246     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
1247        "(i == %d)\n", y_after, i);
1248   }
1249
1250   /*
1251    * test a richedit box that will scroll. There are two general
1252    * cases: the case without any long lines and the case with a long
1253    * line.
1254    */
1255   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1256     if (i == 0)
1257       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1258     else
1259       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1260                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1261                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1262                   "LONG LINE \nb\nc\nd\ne");
1263     for (j = 0; j < 12; j++) /* reset scroll position to top */
1264       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1265
1266     /* get first visible line */
1267     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1268     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1269
1270     /* get new current first visible line */
1271     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1272
1273     ok(((r & 0xffffff00) == 0x00010000) &&
1274        ((r & 0x000000ff) != 0x00000000),
1275        "EM_SCROLL page down didn't scroll by a small positive number of "
1276        "lines (r == 0x%08x)\n", r);
1277     ok(y_after > y_before, "EM_SCROLL page down not functioning "
1278        "(line %d scrolled to line %d\n", y_before, y_after);
1279
1280     y_before = y_after;
1281     
1282     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1283     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1284     ok(((r & 0xffffff00) == 0x0001ff00),
1285        "EM_SCROLL page up didn't scroll by a small negative number of lines "
1286        "(r == 0x%08x)\n", r);
1287     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1288        "%d scrolled to line %d\n", y_before, y_after);
1289     
1290     y_before = y_after;
1291
1292     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1293
1294     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1295
1296     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1297        "(r == 0x%08x)\n", r);
1298     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1299        "1 line (%d scrolled to %d)\n", y_before, y_after);
1300
1301     y_before = y_after;
1302
1303     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1304
1305     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1306
1307     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1308        "(r == 0x%08x)\n", r);
1309     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1310        "line (%d scrolled to %d)\n", y_before, y_after);
1311
1312     y_before = y_after;
1313
1314     r = SendMessage(hwndRichEdit, EM_SCROLL,
1315                     SB_LINEUP, 0); /* lineup beyond top */
1316
1317     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1318
1319     ok(r == 0x00010000,
1320        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1321     ok(y_before == y_after,
1322        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1323
1324     y_before = y_after;
1325
1326     r = SendMessage(hwndRichEdit, EM_SCROLL,
1327                     SB_PAGEUP, 0);/*page up beyond top */
1328
1329     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1330
1331     ok(r == 0x00010000,
1332        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1333     ok(y_before == y_after,
1334        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1335
1336     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1337       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1338     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1339     r = SendMessage(hwndRichEdit, EM_SCROLL,
1340                     SB_PAGEDOWN, 0); /* page down beyond bot */
1341     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1342
1343     ok(r == 0x00010000,
1344        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1345     ok(y_before == y_after,
1346        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1347        y_before, y_after);
1348
1349     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1350     SendMessage(hwndRichEdit, EM_SCROLL,
1351                 SB_LINEDOWN, 0); /* line down beyond bot */
1352     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1353     
1354     ok(r == 0x00010000,
1355        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1356     ok(y_before == y_after,
1357        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1358        y_before, y_after);
1359   }
1360   DestroyWindow(hwndRichEdit);
1361 }
1362
1363 static void test_EM_SETUNDOLIMIT(void)
1364 {
1365   /* cases we test for:
1366    * default behaviour - limiting at 100 undo's 
1367    * undo disabled - setting a limit of 0
1368    * undo limited -  undo limit set to some to some number, like 2
1369    * bad input - sending a negative number should default to 100 undo's */
1370  
1371   HWND hwndRichEdit = new_richedit(NULL);
1372   CHARRANGE cr;
1373   int i;
1374   int result;
1375   
1376   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1377   cr.cpMin = 0;
1378   cr.cpMax = 1;
1379   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1380     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1381       also, multiple pastes don't combine like WM_CHAR would */
1382   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1383
1384   /* first case - check the default */
1385   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1386   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1387     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1388   for (i=0; i<100; i++) /* Undo 100 of them */
1389     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1390   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1391      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1392
1393   /* second case - cannot undo */
1394   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1395   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
1396   SendMessage(hwndRichEdit,
1397               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1398   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1399      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1400
1401   /* third case - set it to an arbitrary number */
1402   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
1403   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
1404   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1405   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1406   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
1407   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1408   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1409      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1410   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1411   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1412      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1413   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
1414   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1415      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1416   
1417   /* fourth case - setting negative numbers should default to 100 undos */
1418   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
1419   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1420   ok (result == 100, 
1421       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1422       
1423   DestroyWindow(hwndRichEdit);
1424 }
1425
1426 static void test_ES_PASSWORD(void)
1427 {
1428   /* This isn't hugely testable, so we're just going to run it through its paces */
1429
1430   HWND hwndRichEdit = new_richedit(NULL);
1431   WCHAR result;
1432
1433   /* First, check the default of a regular control */
1434   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1435   ok (result == 0,
1436         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1437
1438   /* Now, set it to something normal */
1439   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1440   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1441   ok (result == 120,
1442         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1443
1444   /* Now, set it to something odd */
1445   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1446   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1447   ok (result == 1234,
1448         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1449   DestroyWindow(hwndRichEdit);
1450 }
1451
1452 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1453                                          LPBYTE pbBuff,
1454                                          LONG cb,
1455                                          LONG *pcb)
1456 {
1457   char** str = (char**)dwCookie;
1458   *pcb = cb;
1459   if (*pcb > 0) {
1460     memcpy(*str, pbBuff, *pcb);
1461     *str += *pcb;
1462   }
1463   return 0;
1464 }
1465
1466 static void test_WM_SETTEXT()
1467 {
1468   HWND hwndRichEdit = new_richedit(NULL);
1469   const char * TestItem1 = "TestSomeText";
1470   const char * TestItem2 = "TestSomeText\r";
1471   const char * TestItem2_after = "TestSomeText\r\n";
1472   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1473   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1474   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1475   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1476   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1477   const char * TestItem5_after = "TestSomeText TestSomeText";
1478   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1479   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1480   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1481   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1482
1483   char buf[1024] = {0};
1484   LRESULT result;
1485   EDITSTREAM es;
1486   char * p;
1487
1488   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1489      any solitary \r to be converted to \r\n on return. Properly paired
1490      \r\n are not affected. It also shows that the special sequence \r\r\n
1491      gets converted to a single space.
1492    */
1493
1494 #define TEST_SETTEXT(a, b) \
1495   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1496   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1497   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1498   ok (result == lstrlen(buf), \
1499         "WM_GETTEXT returned %ld instead of expected %u\n", \
1500         result, lstrlen(buf)); \
1501   result = strcmp(b, buf); \
1502   ok(result == 0, \
1503         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1504
1505   TEST_SETTEXT(TestItem1, TestItem1)
1506   TEST_SETTEXT(TestItem2, TestItem2_after)
1507   TEST_SETTEXT(TestItem3, TestItem3_after)
1508   TEST_SETTEXT(TestItem3_after, TestItem3_after)
1509   TEST_SETTEXT(TestItem4, TestItem4_after)
1510   TEST_SETTEXT(TestItem5, TestItem5_after)
1511   TEST_SETTEXT(TestItem6, TestItem6_after)
1512   TEST_SETTEXT(TestItem7, TestItem7_after)
1513
1514   /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1515   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1516   p = buf;
1517   es.dwCookie = (DWORD_PTR)&p;
1518   es.dwError = 0;
1519   es.pfnCallback = test_WM_SETTEXT_esCallback;
1520   memset(buf, 0, sizeof(buf));
1521   SendMessage(hwndRichEdit, EM_STREAMOUT,
1522               (WPARAM)(SF_RTF), (LPARAM)&es);
1523   trace("EM_STREAMOUT produced: \n%s\n", buf);
1524   TEST_SETTEXT(buf, TestItem1)
1525
1526 #undef TEST_SETTEXT
1527   DestroyWindow(hwndRichEdit);
1528 }
1529
1530 static void test_EM_STREAMOUT(void)
1531 {
1532   HWND hwndRichEdit = new_richedit(NULL);
1533   int r;
1534   EDITSTREAM es;
1535   char buf[1024] = {0};
1536   char * p;
1537
1538   const char * TestItem1 = "TestSomeText";
1539   const char * TestItem2 = "TestSomeText\r";
1540   const char * TestItem3 = "TestSomeText\r\n";
1541
1542   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1543   p = buf;
1544   es.dwCookie = (DWORD_PTR)&p;
1545   es.dwError = 0;
1546   es.pfnCallback = test_WM_SETTEXT_esCallback;
1547   memset(buf, 0, sizeof(buf));
1548   SendMessage(hwndRichEdit, EM_STREAMOUT,
1549               (WPARAM)(SF_TEXT), (LPARAM)&es);
1550   r = strlen(buf);
1551   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
1552   ok(strcmp(buf, TestItem1) == 0,
1553         "streamed text different, got %s\n", buf);
1554
1555   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
1556   p = buf;
1557   es.dwCookie = (DWORD_PTR)&p;
1558   es.dwError = 0;
1559   es.pfnCallback = test_WM_SETTEXT_esCallback;
1560   memset(buf, 0, sizeof(buf));
1561   SendMessage(hwndRichEdit, EM_STREAMOUT,
1562               (WPARAM)(SF_TEXT), (LPARAM)&es);
1563   r = strlen(buf);
1564   /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
1565   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1566   ok(strcmp(buf, TestItem3) == 0,
1567         "streamed text different from, got %s\n", buf);
1568   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1569   p = buf;
1570   es.dwCookie = (DWORD_PTR)&p;
1571   es.dwError = 0;
1572   es.pfnCallback = test_WM_SETTEXT_esCallback;
1573   memset(buf, 0, sizeof(buf));
1574   SendMessage(hwndRichEdit, EM_STREAMOUT,
1575               (WPARAM)(SF_TEXT), (LPARAM)&es);
1576   r = strlen(buf);
1577   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1578   ok(strcmp(buf, TestItem3) == 0,
1579         "streamed text different, got %s\n", buf);
1580
1581   DestroyWindow(hwndRichEdit);
1582 }
1583
1584 static void test_EM_SETTEXTEX(void)
1585 {
1586   HWND hwndRichEdit = new_richedit(NULL);
1587   SETTEXTEX setText;
1588   GETTEXTEX getText;
1589   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1590                        'S', 'o', 'm', 'e', 
1591                        'T', 'e', 'x', 't', 0}; 
1592   WCHAR TestItem2[] = {'T', 'e', 's', 't',
1593                        'S', 'o', 'm', 'e',
1594                        'T', 'e', 'x', 't',
1595                       '\r', 0};
1596   const char * TestItem2_after = "TestSomeText\r\n";
1597   WCHAR TestItem3[] = {'T', 'e', 's', 't',
1598                        'S', 'o', 'm', 'e',
1599                        'T', 'e', 'x', 't',
1600                       '\r','\n','\r','\n', 0};
1601   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1602                        'S', 'o', 'm', 'e',
1603                        'T', 'e', 'x', 't',
1604                        '\n','\n', 0};
1605   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1606                        'S', 'o', 'm', 'e',
1607                        'T', 'e', 'x', 't',
1608                        '\r','\r', 0};
1609   WCHAR TestItem4[] = {'T', 'e', 's', 't',
1610                        'S', 'o', 'm', 'e',
1611                        'T', 'e', 'x', 't',
1612                       '\r','\r','\n','\r',
1613                       '\n', 0};
1614   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1615                        'S', 'o', 'm', 'e',
1616                        'T', 'e', 'x', 't',
1617                        ' ','\r', 0};
1618 #define MAX_BUF_LEN 1024
1619   WCHAR buf[MAX_BUF_LEN];
1620   char * p;
1621   int result;
1622   CHARRANGE cr;
1623   EDITSTREAM es;
1624
1625   setText.codepage = 1200;  /* no constant for unicode */
1626   getText.codepage = 1200;  /* no constant for unicode */
1627   getText.cb = MAX_BUF_LEN;
1628   getText.flags = GT_DEFAULT;
1629   getText.lpDefaultChar = NULL;
1630   getText.lpUsedDefChar = NULL;
1631
1632   setText.flags = 0;
1633   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1634   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1635   ok(lstrcmpW(buf, TestItem1) == 0,
1636       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1637
1638   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1639      convert \r to \r\n on return
1640    */
1641   setText.codepage = 1200;  /* no constant for unicode */
1642   getText.codepage = 1200;  /* no constant for unicode */
1643   getText.cb = MAX_BUF_LEN;
1644   getText.flags = GT_DEFAULT;
1645   getText.lpDefaultChar = NULL;
1646   getText.lpUsedDefChar = NULL;
1647   setText.flags = 0;
1648   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1649   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1650   ok(lstrcmpW(buf, TestItem2) == 0,
1651       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1652
1653   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1654   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1655   ok(strcmp((const char *)buf, TestItem2_after) == 0,
1656       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1657
1658   /* Baseline test for just-enough buffer space for string */
1659   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1660   getText.codepage = 1200;  /* no constant for unicode */
1661   getText.flags = GT_DEFAULT;
1662   getText.lpDefaultChar = NULL;
1663   getText.lpUsedDefChar = NULL;
1664   memset(buf, 0, MAX_BUF_LEN);
1665   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1666   ok(lstrcmpW(buf, TestItem2) == 0,
1667       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1668
1669   /* When there is enough space for one character, but not both, of the CRLF
1670      pair at the end of the string, the CR is not copied at all. That is,
1671      the caller must not see CRLF pairs truncated to CR at the end of the
1672      string.
1673    */
1674   getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1675   getText.codepage = 1200;  /* no constant for unicode */
1676   getText.flags = GT_USECRLF;   /* <-- asking for CR -> CRLF conversion */
1677   getText.lpDefaultChar = NULL;
1678   getText.lpUsedDefChar = NULL;
1679   memset(buf, 0, MAX_BUF_LEN);
1680   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1681   ok(lstrcmpW(buf, TestItem1) == 0,
1682       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1683
1684
1685   /* \r\n pairs get changed into \r */
1686   setText.codepage = 1200;  /* no constant for unicode */
1687   getText.codepage = 1200;  /* no constant for unicode */
1688   getText.cb = MAX_BUF_LEN;
1689   getText.flags = GT_DEFAULT;
1690   getText.lpDefaultChar = NULL;
1691   getText.lpUsedDefChar = NULL;
1692   setText.flags = 0;
1693   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1694   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1695   ok(lstrcmpW(buf, TestItem3_after) == 0,
1696       "EM_SETTEXTEX did not convert properly\n");
1697
1698   /* \n also gets changed to \r */
1699   setText.codepage = 1200;  /* no constant for unicode */
1700   getText.codepage = 1200;  /* no constant for unicode */
1701   getText.cb = MAX_BUF_LEN;
1702   getText.flags = GT_DEFAULT;
1703   getText.lpDefaultChar = NULL;
1704   getText.lpUsedDefChar = NULL;
1705   setText.flags = 0;
1706   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1707   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1708   ok(lstrcmpW(buf, TestItem3_after) == 0,
1709       "EM_SETTEXTEX did not convert properly\n");
1710
1711   /* \r\r\n gets changed into single space */
1712   setText.codepage = 1200;  /* no constant for unicode */
1713   getText.codepage = 1200;  /* no constant for unicode */
1714   getText.cb = MAX_BUF_LEN;
1715   getText.flags = GT_DEFAULT;
1716   getText.lpDefaultChar = NULL;
1717   getText.lpUsedDefChar = NULL;
1718   setText.flags = 0;
1719   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1720   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1721   ok(lstrcmpW(buf, TestItem4_after) == 0,
1722       "EM_SETTEXTEX did not convert properly\n");
1723
1724   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1725                        (WPARAM)&setText, (LPARAM) NULL);
1726   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1727   
1728   ok (result == 1, 
1729       "EM_SETTEXTEX returned %d, instead of 1\n",result);
1730   ok(lstrlenW(buf) == 0,
1731       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1732   
1733   /* put some text back */
1734   setText.flags = 0;
1735   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1736   /* select some text */
1737   cr.cpMax = 1;
1738   cr.cpMin = 3;
1739   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1740   /* replace current selection */
1741   setText.flags = ST_SELECTION;
1742   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1743                        (WPARAM)&setText, (LPARAM) NULL);
1744   ok(result == 0,
1745       "EM_SETTEXTEX with NULL lParam to replace selection"
1746       " with no text should return 0. Got %i\n",
1747       result);
1748   
1749   /* put some text back */
1750   setText.flags = 0;
1751   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1752   /* select some text */
1753   cr.cpMax = 1;
1754   cr.cpMin = 3;
1755   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1756   /* replace current selection */
1757   setText.flags = ST_SELECTION;
1758   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1759                        (WPARAM)&setText, (LPARAM) TestItem1);
1760   /* get text */
1761   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1762   ok(result == lstrlenW(TestItem1),
1763       "EM_SETTEXTEX with NULL lParam to replace selection"
1764       " with no text should return 0. Got %i\n",
1765       result);
1766   ok(lstrlenW(buf) == 22,
1767       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1768       lstrlenW(buf) );
1769
1770   /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1771   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1772   p = (char *)buf;
1773   es.dwCookie = (DWORD_PTR)&p;
1774   es.dwError = 0;
1775   es.pfnCallback = test_WM_SETTEXT_esCallback;
1776   memset(buf, 0, sizeof(buf));
1777   SendMessage(hwndRichEdit, EM_STREAMOUT,
1778               (WPARAM)(SF_RTF), (LPARAM)&es);
1779   trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1780
1781   setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1782   getText.codepage = 1200;  /* no constant for unicode */
1783   getText.cb = MAX_BUF_LEN;
1784   getText.flags = GT_DEFAULT;
1785   getText.lpDefaultChar = NULL;
1786   getText.lpUsedDefChar = NULL;
1787
1788   setText.flags = 0;
1789   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1790   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1791   ok(lstrcmpW(buf, TestItem1) == 0,
1792       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1793
1794
1795   DestroyWindow(hwndRichEdit);
1796 }
1797
1798 static void test_EM_LIMITTEXT(void)
1799 {
1800   int ret;
1801
1802   HWND hwndRichEdit = new_richedit(NULL);
1803
1804   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1805    * about setting the length to -1 for multiline edit controls doesn't happen.
1806    */
1807
1808   /* Don't check default gettextlimit case. That's done in other tests */
1809
1810   /* Set textlimit to 100 */
1811   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1812   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1813   ok (ret == 100,
1814       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1815
1816   /* Set textlimit to 0 */
1817   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1818   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1819   ok (ret == 65536,
1820       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1821
1822   /* Set textlimit to -1 */
1823   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1824   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1825   ok (ret == -1,
1826       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1827
1828   /* Set textlimit to -2 */
1829   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1830   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1831   ok (ret == -2,
1832       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1833
1834   DestroyWindow (hwndRichEdit);
1835 }
1836
1837
1838 static void test_EM_EXLIMITTEXT(void)
1839 {
1840   int i, selBegin, selEnd, len1, len2;
1841   int result;
1842   char text[1024 + 1];
1843   char buffer[1024 + 1];
1844   int textlimit = 0; /* multiple of 100 */
1845   HWND hwndRichEdit = new_richedit(NULL);
1846   
1847   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1848   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1849   
1850   textlimit = 256000;
1851   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1852   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1853   /* set higher */
1854   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1855   
1856   textlimit = 1000;
1857   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1858   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1859   /* set lower */
1860   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1861  
1862   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1863   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1864   /* default for WParam = 0 */
1865   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1866  
1867   textlimit = sizeof(text)-1;
1868   memset(text, 'W', textlimit);
1869   text[sizeof(text)-1] = 0;
1870   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1871   /* maxed out text */
1872   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1873   
1874   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1875   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1876   len1 = selEnd - selBegin;
1877   
1878   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1879   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1880   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1881   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1882   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1883   len2 = selEnd - selBegin;
1884   
1885   ok(len1 != len2,
1886     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1887     len1,len2,i);
1888   
1889   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1890   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1891   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1892   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1893   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1894   len1 = selEnd - selBegin;
1895   
1896   ok(len1 != len2,
1897     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1898     len1,len2,i);
1899   
1900   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1901   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1902   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1903   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1904   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1905   len2 = selEnd - selBegin;
1906   
1907   ok(len1 == len2, 
1908     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1909     len1,len2,i);
1910
1911   /* set text up to the limit, select all the text, then add a char */
1912   textlimit = 5;
1913   memset(text, 'W', textlimit);
1914   text[textlimit] = 0;
1915   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1916   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1917   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1918   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1919   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1920   result = strcmp(buffer, "A");
1921   ok(0 == result, "got string = \"%s\"\n", buffer);
1922
1923   /* WM_SETTEXT not limited */
1924   textlimit = 10;
1925   memset(text, 'W', textlimit);
1926   text[textlimit] = 0;
1927   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1928   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1929   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1930   i = strlen(buffer);
1931   ok(10 == i, "expected 10 chars\n");
1932   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1933   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1934
1935   /* try inserting more text at end */
1936   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1937   ok(0 == i, "WM_CHAR wasn't processed\n");
1938   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1939   i = strlen(buffer);
1940   ok(10 == i, "expected 10 chars, got %i\n", i);
1941   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1942   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1943
1944   /* try inserting text at beginning */
1945   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1946   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1947   ok(0 == i, "WM_CHAR wasn't processed\n");
1948   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1949   i = strlen(buffer);
1950   ok(10 == i, "expected 10 chars, got %i\n", i);
1951   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1952   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1953
1954   /* WM_CHAR is limited */
1955   textlimit = 1;
1956   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1957   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1958   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1959   ok(0 == i, "WM_CHAR wasn't processed\n");
1960   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1961   ok(0 == i, "WM_CHAR wasn't processed\n");
1962   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1963   i = strlen(buffer);
1964   ok(1 == i, "expected 1 chars, got %i instead\n", i);
1965
1966   DestroyWindow(hwndRichEdit);
1967 }
1968
1969 static void test_EM_GETLIMITTEXT(void)
1970 {
1971   int i;
1972   HWND hwndRichEdit = new_richedit(NULL);
1973
1974   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1975   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1976
1977   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1978   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1979   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1980
1981   DestroyWindow(hwndRichEdit);
1982 }
1983
1984 static void test_WM_SETFONT(void)
1985 {
1986   /* There is no invalid input or error conditions for this function.
1987    * NULL wParam and lParam just fall back to their default values 
1988    * It should be noted that even if you use a gibberish name for your fonts
1989    * here, it will still work because the name is stored. They will display as
1990    * System, but will report their name to be whatever they were created as */
1991   
1992   HWND hwndRichEdit = new_richedit(NULL);
1993   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1994     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1995     FF_DONTCARE, "Marlett");
1996   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1997     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1998     FF_DONTCARE, "MS Sans Serif");
1999   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2000     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2001     FF_DONTCARE, "Courier");
2002   LOGFONTA sentLogFont;
2003   CHARFORMAT2A returnedCF2A;
2004   
2005   returnedCF2A.cbSize = sizeof(returnedCF2A);
2006   
2007   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2008   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2009   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2010
2011   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2012   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2013     "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2014     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2015
2016   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2017   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2018   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2019   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2020     "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2021     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2022     
2023   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2024   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2025   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2026   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2027     "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2028     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2029    
2030   /* This last test is special since we send in NULL. We clear the variables
2031    * and just compare to "System" instead of the sent in font name. */
2032   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2033   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2034   returnedCF2A.cbSize = sizeof(returnedCF2A);
2035   
2036   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2037   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
2038   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2039   ok (!strcmp("System",returnedCF2A.szFaceName),
2040     "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2041   
2042   DestroyWindow(hwndRichEdit);
2043 }
2044
2045
2046 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2047                                          LPBYTE pbBuff,
2048                                          LONG cb,
2049                                          LONG *pcb)
2050 {
2051   const char** str = (const char**)dwCookie;
2052   int size = strlen(*str);
2053   if(size > 3)  /* let's make it piecemeal for fun */
2054     size = 3;
2055   *pcb = cb;
2056   if (*pcb > size) {
2057     *pcb = size;
2058   }
2059   if (*pcb > 0) {
2060     memcpy(pbBuff, *str, *pcb);
2061     *str += *pcb;
2062   }
2063   return 0;
2064 }
2065
2066 static void test_EM_GETMODIFY(void)
2067 {
2068   HWND hwndRichEdit = new_richedit(NULL);
2069   LRESULT result;
2070   SETTEXTEX setText;
2071   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
2072                        'S', 'o', 'm', 'e', 
2073                        'T', 'e', 'x', 't', 0}; 
2074   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
2075                        'S', 'o', 'm', 'e', 
2076                        'O', 't', 'h', 'e', 'r',
2077                        'T', 'e', 'x', 't', 0}; 
2078   const char* streamText = "hello world";
2079   CHARFORMAT2 cf2;
2080   PARAFORMAT2 pf2;
2081   EDITSTREAM es;
2082   
2083   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
2084     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
2085     FF_DONTCARE, "Courier");
2086   
2087   setText.codepage = 1200;  /* no constant for unicode */
2088   setText.flags = ST_KEEPUNDO;
2089   
2090
2091   /* modify flag shouldn't be set when richedit is first created */
2092   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2093   ok (result == 0, 
2094       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2095   
2096   /* setting modify flag should actually set it */
2097   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2098   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2099   ok (result != 0, 
2100       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2101   
2102   /* clearing modify flag should actually clear it */
2103   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2104   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2105   ok (result == 0, 
2106       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2107  
2108   /* setting font doesn't change modify flag */
2109   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2110   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2111   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2112   ok (result == 0,
2113       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2114
2115   /* setting text should set modify flag */
2116   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2117   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2118   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2119   ok (result != 0,
2120       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2121   
2122   /* undo previous text doesn't reset modify flag */
2123   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2124   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2125   ok (result != 0,
2126       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2127   
2128   /* set text with no flag to keep undo stack should not set modify flag */
2129   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2130   setText.flags = 0;
2131   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2132   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2133   ok (result == 0,
2134       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2135   
2136   /* WM_SETTEXT doesn't modify */
2137   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2138   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2139   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2140   ok (result == 0,
2141       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2142   
2143   /* clear the text */
2144   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2145   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2146   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2147   ok (result == 0,
2148       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2149   
2150   /* replace text */
2151   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2152   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2153   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2154   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
2155   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2156   ok (result != 0,
2157       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
2158   
2159   /* copy/paste text 1 */
2160   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2161   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2162   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2163   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2164   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2165   ok (result != 0,
2166       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
2167   
2168   /* copy/paste text 2 */
2169   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2170   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2171   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2172   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
2173   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2174   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2175   ok (result != 0,
2176       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
2177   
2178   /* press char */
2179   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2180   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
2181   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2182   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2183   ok (result != 0,
2184       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
2185
2186   /* press del */
2187   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2188   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2189   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
2190   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2191   ok (result != 0,
2192       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
2193   
2194   /* set char format */
2195   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2196   cf2.cbSize = sizeof(CHARFORMAT2);
2197   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
2198              (LPARAM) &cf2);
2199   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
2200   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
2201   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2202   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2203   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
2204   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2205   ok (result != 0,
2206       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
2207   
2208   /* set para format */
2209   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2210   pf2.cbSize = sizeof(PARAFORMAT2);
2211   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
2212              (LPARAM) &pf2);
2213   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
2214   pf2.wAlignment = PFA_RIGHT;
2215   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
2216   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2217   ok (result == 0,
2218       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
2219
2220   /* EM_STREAM */
2221   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2222   es.dwCookie = (DWORD_PTR)&streamText;
2223   es.dwError = 0;
2224   es.pfnCallback = test_EM_GETMODIFY_esCallback;
2225   SendMessage(hwndRichEdit, EM_STREAMIN, 
2226               (WPARAM)(SF_TEXT), (LPARAM)&es);
2227   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2228   ok (result != 0,
2229       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
2230
2231   DestroyWindow(hwndRichEdit);
2232 }
2233
2234 struct exsetsel_s {
2235   long min;
2236   long max;
2237   long expected_retval;
2238   int expected_getsel_start;
2239   int expected_getsel_end;
2240   int _exsetsel_todo_wine;
2241   int _getsel_todo_wine;
2242 };
2243
2244 const struct exsetsel_s exsetsel_tests[] = {
2245   /* sanity tests */
2246   {5, 10, 10, 5, 10, 0, 0},
2247   {15, 17, 17, 15, 17, 0, 0},
2248   /* test cpMax > strlen() */
2249   {0, 100, 18, 0, 18, 0, 1},
2250   /* test cpMin == cpMax */
2251   {5, 5, 5, 5, 5, 0, 0},
2252   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2253   {-1, 0, 5, 5, 5, 0, 0},
2254   {-1, 17, 5, 5, 5, 0, 0},
2255   {-1, 18, 5, 5, 5, 0, 0},
2256   /* test cpMin < 0 && cpMax < 0 */
2257   {-1, -1, 17, 17, 17, 0, 0},
2258   {-4, -5, 17, 17, 17, 0, 0},
2259   /* test cMin >=0 && cpMax < 0 (bug 6814) */
2260   {0, -1, 18, 0, 18, 0, 1},
2261   {17, -5, 18, 17, 18, 0, 1},
2262   {18, -3, 17, 17, 17, 0, 0},
2263   /* test if cpMin > cpMax */
2264   {15, 19, 18, 15, 18, 0, 1},
2265   {19, 15, 18, 15, 18, 0, 1}
2266 };
2267
2268 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2269     CHARRANGE cr;
2270     long result;
2271     int start, end;
2272
2273     cr.cpMin = setsel->min;
2274     cr.cpMax = setsel->max;
2275     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2276
2277     if (setsel->_exsetsel_todo_wine) {
2278         todo_wine {
2279             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2280         }
2281     } else {
2282         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2283     }
2284
2285     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2286
2287     if (setsel->_getsel_todo_wine) {
2288         todo_wine {
2289             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);
2290         }
2291     } else {
2292         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);
2293     }
2294 }
2295
2296 static void test_EM_EXSETSEL(void)
2297 {
2298     HWND hwndRichEdit = new_richedit(NULL);
2299     int i;
2300     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2301
2302     /* sending some text to the window */
2303     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2304     /*                                                 01234567890123456*/
2305     /*                                                          10      */
2306
2307     for (i = 0; i < num_tests; i++) {
2308         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2309     }
2310
2311     DestroyWindow(hwndRichEdit);
2312 }
2313
2314 static void test_EM_REPLACESEL(int redraw)
2315 {
2316     HWND hwndRichEdit = new_richedit(NULL);
2317     char buffer[1024] = {0};
2318     int r;
2319     GETTEXTEX getText;
2320     CHARRANGE cr;
2321
2322     /* sending some text to the window */
2323     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2324     /*                                                 01234567890123456*/
2325     /*                                                          10      */
2326
2327     /* FIXME add more tests */
2328     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2329     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2330     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2331     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2332     r = strcmp(buffer, "testing");
2333     ok(0 == r, "expected %d, got %d\n", 0, r);
2334
2335     DestroyWindow(hwndRichEdit);
2336
2337     hwndRichEdit = new_richedit(NULL);
2338
2339     trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2340     SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2341
2342     /* Test behavior with carriage returns and newlines */
2343     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2344     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2345     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2346     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2347     r = strcmp(buffer, "RichEdit1");
2348     ok(0 == r, "expected %d, got %d\n", 0, r);
2349     getText.cb = 1024;
2350     getText.codepage = CP_ACP;
2351     getText.flags = GT_DEFAULT;
2352     getText.lpDefaultChar = NULL;
2353     getText.lpUsedDefChar = NULL;
2354     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2355     ok(strcmp(buffer, "RichEdit1") == 0,
2356       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2357
2358     /* Test number of lines reported after EM_REPLACESEL */
2359     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2360     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2361
2362     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2363     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2364     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2365     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2366     r = strcmp(buffer, "RichEdit1\r\n");
2367     ok(0 == r, "expected %d, got %d\n", 0, r);
2368     getText.cb = 1024;
2369     getText.codepage = CP_ACP;
2370     getText.flags = GT_DEFAULT;
2371     getText.lpDefaultChar = NULL;
2372     getText.lpUsedDefChar = NULL;
2373     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2374     ok(strcmp(buffer, "RichEdit1\r") == 0,
2375       "EM_GETTEXTEX returned incorrect string\n");
2376
2377     /* Test number of lines reported after EM_REPLACESEL */
2378     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2379     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2380
2381     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2382        EM_REPLACESEL. The general rule seems to be that Win98's riched20
2383        returns the number of characters *inserted* into the control (after
2384        required conversions), but WinXP's riched20 returns the number of
2385        characters interpreted from the original lParam. Wine's builtin riched20
2386        implements the WinXP behavior.
2387      */
2388     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2389     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2390     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2391         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2392
2393     /* Test number of lines reported after EM_REPLACESEL */
2394     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2395     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2396
2397     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2398     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2399     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2400     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2401
2402     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2403     r = strcmp(buffer, "RichEdit1\r\n");
2404     ok(0 == r, "expected %d, got %d\n", 0, r);
2405     getText.cb = 1024;
2406     getText.codepage = CP_ACP;
2407     getText.flags = GT_DEFAULT;
2408     getText.lpDefaultChar = NULL;
2409     getText.lpUsedDefChar = NULL;
2410     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2411     ok(strcmp(buffer, "RichEdit1\r") == 0,
2412       "EM_GETTEXTEX returned incorrect string\n");
2413
2414     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2415     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2416     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2417     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2418
2419     /* The following tests show that richedit should handle the special \r\r\n
2420        sequence by turning it into a single space on insertion. However,
2421        EM_REPLACESEL on WinXP returns the number of characters in the original
2422        string.
2423      */
2424
2425     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2426     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2427     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2428     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2429     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2430     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2431     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2432
2433     /* Test the actual string */
2434     getText.cb = 1024;
2435     getText.codepage = CP_ACP;
2436     getText.flags = GT_DEFAULT;
2437     getText.lpDefaultChar = NULL;
2438     getText.lpUsedDefChar = NULL;
2439     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2440     ok(strcmp(buffer, "\r\r") == 0,
2441       "EM_GETTEXTEX returned incorrect string\n");
2442
2443     /* Test number of lines reported after EM_REPLACESEL */
2444     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2445     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2446
2447     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2448     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2449     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2450         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2451     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2452     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2453     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2454     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2455
2456     /* Test the actual string */
2457     getText.cb = 1024;
2458     getText.codepage = CP_ACP;
2459     getText.flags = GT_DEFAULT;
2460     getText.lpDefaultChar = NULL;
2461     getText.lpUsedDefChar = NULL;
2462     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2463     ok(strcmp(buffer, " ") == 0,
2464       "EM_GETTEXTEX returned incorrect string\n");
2465
2466     /* Test number of lines reported after EM_REPLACESEL */
2467     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2468     ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2469
2470     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2471     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2472     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2473         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2474     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2475     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2476     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2477     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2478
2479     /* Test the actual string */
2480     getText.cb = 1024;
2481     getText.codepage = CP_ACP;
2482     getText.flags = GT_DEFAULT;
2483     getText.lpDefaultChar = NULL;
2484     getText.lpUsedDefChar = NULL;
2485     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2486     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2487       "EM_GETTEXTEX returned incorrect string\n");
2488
2489     /* Test number of lines reported after EM_REPLACESEL */
2490     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2491     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2492
2493     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2494     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2495     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2496         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2497     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2498     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2499     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2500     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2501
2502     /* Test the actual string */
2503     getText.cb = 1024;
2504     getText.codepage = CP_ACP;
2505     getText.flags = GT_DEFAULT;
2506     getText.lpDefaultChar = NULL;
2507     getText.lpUsedDefChar = NULL;
2508     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2509     ok(strcmp(buffer, " \r") == 0,
2510       "EM_GETTEXTEX returned incorrect string\n");
2511
2512     /* Test number of lines reported after EM_REPLACESEL */
2513     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2514     ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2515
2516     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2517     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2518     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2519         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2520     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2521     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2522     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2523     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2524
2525     /* Test the actual string */
2526     getText.cb = 1024;
2527     getText.codepage = CP_ACP;
2528     getText.flags = GT_DEFAULT;
2529     getText.lpDefaultChar = NULL;
2530     getText.lpUsedDefChar = NULL;
2531     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2532     ok(strcmp(buffer, " \r\r") == 0,
2533       "EM_GETTEXTEX returned incorrect string\n");
2534
2535     /* Test number of lines reported after EM_REPLACESEL */
2536     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2537     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2538
2539     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2540     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2541     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2542         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2543     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2544     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2545     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2546     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2547
2548     /* Test the actual string */
2549     getText.cb = 1024;
2550     getText.codepage = CP_ACP;
2551     getText.flags = GT_DEFAULT;
2552     getText.lpDefaultChar = NULL;
2553     getText.lpUsedDefChar = NULL;
2554     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2555     ok(strcmp(buffer, "\rX\r\r\r") == 0,
2556       "EM_GETTEXTEX returned incorrect string\n");
2557
2558     /* Test number of lines reported after EM_REPLACESEL */
2559     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2560     ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2561
2562     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2563     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2564     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2565     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2566     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2567     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2568     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2569
2570     /* Test the actual string */
2571     getText.cb = 1024;
2572     getText.codepage = CP_ACP;
2573     getText.flags = GT_DEFAULT;
2574     getText.lpDefaultChar = NULL;
2575     getText.lpUsedDefChar = NULL;
2576     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2577     ok(strcmp(buffer, "\r\r") == 0,
2578       "EM_GETTEXTEX returned incorrect string\n");
2579
2580     /* Test number of lines reported after EM_REPLACESEL */
2581     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2582     ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2583
2584     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2585     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2586     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2587         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2588     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2589     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2590     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2591     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2592
2593     /* Test the actual string */
2594     getText.cb = 1024;
2595     getText.codepage = CP_ACP;
2596     getText.flags = GT_DEFAULT;
2597     getText.lpDefaultChar = NULL;
2598     getText.lpUsedDefChar = NULL;
2599     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2600     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2601       "EM_GETTEXTEX returned incorrect string\n");
2602
2603     /* Test number of lines reported after EM_REPLACESEL */
2604     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2605     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2606
2607     DestroyWindow(hwndRichEdit);
2608 }
2609
2610 static void test_WM_PASTE(void)
2611 {
2612     MSG msg;
2613     int result;
2614     char buffer[1024] = {0};
2615     char key_info[][3] =
2616     {
2617         /* VirtualKey, ScanCode, WM_CHAR code */
2618         {'C', 0x2e,  3},        /* Ctrl-C */
2619         {'X', 0x2d, 24},        /* Ctrl-X */
2620         {'V', 0x2f, 22},        /* Ctrl-V */
2621         {'Z', 0x2c, 26},        /* Ctrl-Z */
2622         {'Y', 0x15, 25},        /* Ctrl-Y */
2623     };
2624     const char* text1 = "testing paste\r";
2625     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2626     const char* text1_after = "testing paste\r\n";
2627     const char* text2 = "testing paste\r\rtesting paste";
2628     const char* text2_after = "testing paste\r\n\r\ntesting paste";
2629     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2630     HWND hwndRichEdit = new_richedit(NULL);
2631
2632     /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2633        messages, probably because it inspects the keyboard state itself.
2634        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2635      */
2636 #define SEND_CTRL_KEY(hwnd, k) \
2637     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2638     keybd_event(k[0], k[1], 0, 0);\
2639     keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2640     keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2641     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2642         TranslateMessage(&msg); \
2643         DispatchMessage(&msg); \
2644     }
2645
2646 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2647 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2648 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2649 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2650 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2651
2652     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2653     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2654
2655     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2656     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2657     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2658     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2659     /* Pasted text should be visible at this step */
2660     result = strcmp(text1_step1, buffer);
2661     ok(result == 0,
2662         "test paste: strcmp = %i\n", result);
2663     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2664     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2665     /* Text should be the same as before (except for \r -> \r\n conversion) */
2666     result = strcmp(text1_after, buffer);
2667     ok(result == 0,
2668         "test paste: strcmp = %i\n", result);
2669
2670     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2671     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2672     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2673     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2674     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2675     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2676     /* Pasted text should be visible at this step */
2677     result = strcmp(text3, buffer);
2678     ok(result == 0,
2679         "test paste: strcmp = %i\n", result);
2680     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2681     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2682     /* Text should be the same as before (except for \r -> \r\n conversion) */
2683     result = strcmp(text2_after, buffer);
2684     ok(result == 0,
2685         "test paste: strcmp = %i\n", result);
2686     SEND_CTRL_Y(hwndRichEdit)   /* Redo */
2687     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2688     /* Text should revert to post-paste state */
2689     result = strcmp(buffer,text3);
2690     ok(result == 0,
2691         "test paste: strcmp = %i\n", result);
2692
2693     DestroyWindow(hwndRichEdit);
2694 }
2695
2696 static void test_EM_FORMATRANGE(void)
2697 {
2698   int r;
2699   FORMATRANGE fr;
2700   HDC hdc;
2701   HWND hwndRichEdit = new_richedit(NULL);
2702
2703   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2704
2705   hdc = GetDC(hwndRichEdit);
2706   ok(hdc != NULL, "Could not get HDC\n");
2707
2708   fr.hdc = fr.hdcTarget = hdc;
2709   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2710   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2711   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2712   fr.chrg.cpMin = 0;
2713   fr.chrg.cpMax = 20;
2714
2715   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2716   todo_wine {
2717     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2718   }
2719
2720   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2721   todo_wine {
2722     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2723   }
2724
2725   fr.chrg.cpMin = 0;
2726   fr.chrg.cpMax = 10;
2727
2728   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2729   todo_wine {
2730     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2731   }
2732
2733   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2734   todo_wine {
2735     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2736   }
2737
2738   DestroyWindow(hwndRichEdit);
2739 }
2740
2741 static int nCallbackCount = 0;
2742
2743 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2744                                  LONG cb, LONG* pcb)
2745 {
2746   const char text[] = {'t','e','s','t'};
2747
2748   if (sizeof(text) <= cb)
2749   {
2750     if ((int)dwCookie != nCallbackCount)
2751     {
2752       *pcb = 0;
2753       return 0;
2754     }
2755
2756     memcpy (pbBuff, text, sizeof(text));
2757     *pcb = sizeof(text);
2758
2759     nCallbackCount++;
2760
2761     return 0;
2762   }
2763   else
2764     return 1; /* indicates callback failed */
2765 }
2766
2767 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2768                                          LPBYTE pbBuff,
2769                                          LONG cb,
2770                                          LONG *pcb)
2771 {
2772   const char** str = (const char**)dwCookie;
2773   int size = strlen(*str);
2774   *pcb = cb;
2775   if (*pcb > size) {
2776     *pcb = size;
2777   }
2778   if (*pcb > 0) {
2779     memcpy(pbBuff, *str, *pcb);
2780     *str += *pcb;
2781   }
2782   return 0;
2783 }
2784
2785
2786 static void test_EM_STREAMIN(void)
2787 {
2788   HWND hwndRichEdit = new_richedit(NULL);
2789   LRESULT result;
2790   EDITSTREAM es;
2791   char buffer[1024] = {0};
2792
2793   const char * streamText0 = "{\\rtf1 TestSomeText}";
2794   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2795   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2796
2797   const char * streamText1 =
2798   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2799   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2800   "}\r\n";
2801
2802   /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2803   const char * streamText2 =
2804     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2805     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2806     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2807     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2808     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2809     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2810     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2811
2812   const char * streamText3 = "RichEdit1";
2813
2814   /* Minimal test without \par at the end */
2815   es.dwCookie = (DWORD_PTR)&streamText0;
2816   es.dwError = 0;
2817   es.pfnCallback = test_EM_STREAMIN_esCallback;
2818   SendMessage(hwndRichEdit, EM_STREAMIN,
2819               (WPARAM)(SF_RTF), (LPARAM)&es);
2820
2821   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2822   ok (result  == 12,
2823       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2824   result = strcmp (buffer,"TestSomeText");
2825   ok (result  == 0,
2826       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2827   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
2828
2829   /* Native richedit 2.0 ignores last \par */
2830   es.dwCookie = (DWORD_PTR)&streamText0a;
2831   es.dwError = 0;
2832   es.pfnCallback = test_EM_STREAMIN_esCallback;
2833   SendMessage(hwndRichEdit, EM_STREAMIN,
2834               (WPARAM)(SF_RTF), (LPARAM)&es);
2835
2836   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2837   ok (result  == 12,
2838       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2839   result = strcmp (buffer,"TestSomeText");
2840   ok (result  == 0,
2841       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2842   ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
2843
2844   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2845   es.dwCookie = (DWORD_PTR)&streamText0b;
2846   es.dwError = 0;
2847   es.pfnCallback = test_EM_STREAMIN_esCallback;
2848   SendMessage(hwndRichEdit, EM_STREAMIN,
2849               (WPARAM)(SF_RTF), (LPARAM)&es);
2850
2851   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2852   ok (result  == 14,
2853       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2854   result = strcmp (buffer,"TestSomeText\r\n");
2855   ok (result  == 0,
2856       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2857   ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
2858
2859   es.dwCookie = (DWORD_PTR)&streamText1;
2860   es.dwError = 0;
2861   es.pfnCallback = test_EM_STREAMIN_esCallback;
2862   SendMessage(hwndRichEdit, EM_STREAMIN,
2863               (WPARAM)(SF_RTF), (LPARAM)&es);
2864
2865   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2866   ok (result  == 12,
2867       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2868   result = strcmp (buffer,"TestSomeText");
2869   ok (result  == 0,
2870       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2871   ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
2872
2873   es.dwCookie = (DWORD_PTR)&streamText2;
2874   es.dwError = 0;
2875   SendMessage(hwndRichEdit, EM_STREAMIN,
2876               (WPARAM)(SF_RTF), (LPARAM)&es);
2877
2878   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2879   ok (result  == 0,
2880       "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2881   ok (strlen(buffer)  == 0,
2882       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2883   ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
2884
2885   es.dwCookie = (DWORD_PTR)&streamText3;
2886   es.dwError = 0;
2887   SendMessage(hwndRichEdit, EM_STREAMIN,
2888               (WPARAM)(SF_RTF), (LPARAM)&es);
2889
2890   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2891   ok (result  == 0,
2892       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
2893   ok (strlen(buffer)  == 0,
2894       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2895   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
2896
2897   DestroyWindow(hwndRichEdit);
2898 }
2899
2900 static void test_EM_StreamIn_Undo(void)
2901 {
2902   /* The purpose of this test is to determine when a EM_StreamIn should be
2903    * undoable. This is important because WM_PASTE currently uses StreamIn and
2904    * pasting should always be undoable but streaming isn't always.
2905    *
2906    * cases to test:
2907    * StreamIn plain text without SFF_SELECTION.
2908    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2909    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2910    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2911    * Feel free to add tests for other text modes or StreamIn things.
2912    */
2913
2914
2915   HWND hwndRichEdit = new_richedit(NULL);
2916   LRESULT result;
2917   EDITSTREAM es;
2918   char buffer[1024] = {0};
2919   const char randomtext[] = "Some text";
2920
2921   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2922
2923   /* StreamIn, no SFF_SELECTION */
2924   es.dwCookie = nCallbackCount;
2925   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2926   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2927   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2928   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2929   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2930   result = strcmp (buffer,"test");
2931   ok (result  == 0,
2932       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2933
2934   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2935   ok (result == FALSE,
2936       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2937
2938   /* StreamIn, SFF_SELECTION, but nothing selected */
2939   es.dwCookie = nCallbackCount;
2940   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2941   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2942   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2943   SendMessage(hwndRichEdit, EM_STREAMIN,
2944               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2945   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2946   result = strcmp (buffer,"testSome text");
2947   ok (result  == 0,
2948       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2949
2950   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2951   ok (result == TRUE,
2952      "EM_STREAMIN with SFF_SELECTION but no selection set "
2953       "should create an undo\n");
2954
2955   /* StreamIn, SFF_SELECTION, with a selection */
2956   es.dwCookie = nCallbackCount;
2957   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2958   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2959   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2960   SendMessage(hwndRichEdit, EM_STREAMIN,
2961               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2962   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2963   result = strcmp (buffer,"Sometesttext");
2964   ok (result  == 0,
2965       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2966
2967   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2968   ok (result == TRUE,
2969       "EM_STREAMIN with SFF_SELECTION and selection set "
2970       "should create an undo\n");
2971
2972 }
2973
2974 static BOOL is_em_settextex_supported(HWND hwnd)
2975 {
2976     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2977     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2978 }
2979
2980 static void test_unicode_conversions(void)
2981 {
2982     static const WCHAR tW[] = {'t',0};
2983     static const WCHAR teW[] = {'t','e',0};
2984     static const WCHAR textW[] = {'t','e','s','t',0};
2985     static const char textA[] = "test";
2986     char bufA[64];
2987     WCHAR bufW[64];
2988     HWND hwnd;
2989     int is_win9x, em_settextex_supported, ret;
2990
2991     is_win9x = GetVersion() & 0x80000000;
2992
2993 #define set_textA(hwnd, wm_set_text, txt) \
2994     do { \
2995         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2996         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2997         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2998         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2999         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3000     } while(0)
3001 #define expect_textA(hwnd, wm_get_text, txt) \
3002     do { \
3003         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3004         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3005         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3006         memset(bufA, 0xAA, sizeof(bufA)); \
3007         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3008         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3009         ret = lstrcmpA(bufA, txt); \
3010         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3011     } while(0)
3012
3013 #define set_textW(hwnd, wm_set_text, txt) \
3014     do { \
3015         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3016         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3017         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3018         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3019         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3020     } while(0)
3021 #define expect_textW(hwnd, wm_get_text, txt) \
3022     do { \
3023         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3024         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3025         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3026         memset(bufW, 0xAA, sizeof(bufW)); \
3027         if (is_win9x) \
3028         { \
3029             assert(wm_get_text == EM_GETTEXTEX); \
3030             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3031             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3032         } \
3033         else \
3034         { \
3035             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3036             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3037         } \
3038         ret = lstrcmpW(bufW, txt); \
3039         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3040     } while(0)
3041 #define expect_empty(hwnd, wm_get_text) \
3042     do { \
3043         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3044         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3045         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3046         memset(bufA, 0xAA, sizeof(bufA)); \
3047         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3048         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3049         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3050     } while(0)
3051
3052     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3053                            0, 0, 200, 60, 0, 0, 0, 0);
3054     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3055
3056     ret = IsWindowUnicode(hwnd);
3057     if (is_win9x)
3058         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3059     else
3060         ok(ret, "RichEdit20W should be unicode under NT\n");
3061
3062     /* EM_SETTEXTEX is supported starting from version 3.0 */
3063     em_settextex_supported = is_em_settextex_supported(hwnd);
3064     trace("EM_SETTEXTEX is %ssupported on this platform\n",
3065           em_settextex_supported ? "" : "NOT ");
3066
3067     expect_empty(hwnd, WM_GETTEXT);
3068     expect_empty(hwnd, EM_GETTEXTEX);
3069
3070     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3071     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3072     expect_textA(hwnd, WM_GETTEXT, "t");
3073     expect_textA(hwnd, EM_GETTEXTEX, "t");
3074     expect_textW(hwnd, EM_GETTEXTEX, tW);
3075
3076     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3077     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3078     expect_textA(hwnd, WM_GETTEXT, "te");
3079     expect_textA(hwnd, EM_GETTEXTEX, "te");
3080     expect_textW(hwnd, EM_GETTEXTEX, teW);
3081
3082     set_textA(hwnd, WM_SETTEXT, NULL);
3083     expect_empty(hwnd, WM_GETTEXT);
3084     expect_empty(hwnd, EM_GETTEXTEX);
3085
3086     if (is_win9x)
3087         set_textA(hwnd, WM_SETTEXT, textW);
3088     else
3089         set_textA(hwnd, WM_SETTEXT, textA);
3090     expect_textA(hwnd, WM_GETTEXT, textA);
3091     expect_textA(hwnd, EM_GETTEXTEX, textA);
3092     expect_textW(hwnd, EM_GETTEXTEX, textW);
3093
3094     if (em_settextex_supported)
3095     {
3096         set_textA(hwnd, EM_SETTEXTEX, textA);
3097         expect_textA(hwnd, WM_GETTEXT, textA);
3098         expect_textA(hwnd, EM_GETTEXTEX, textA);
3099         expect_textW(hwnd, EM_GETTEXTEX, textW);
3100     }
3101
3102     if (!is_win9x)
3103     {
3104         set_textW(hwnd, WM_SETTEXT, textW);
3105         expect_textW(hwnd, WM_GETTEXT, textW);
3106         expect_textA(hwnd, WM_GETTEXT, textA);
3107         expect_textW(hwnd, EM_GETTEXTEX, textW);
3108         expect_textA(hwnd, EM_GETTEXTEX, textA);
3109
3110         if (em_settextex_supported)
3111         {
3112             set_textW(hwnd, EM_SETTEXTEX, textW);
3113             expect_textW(hwnd, WM_GETTEXT, textW);
3114             expect_textA(hwnd, WM_GETTEXT, textA);
3115             expect_textW(hwnd, EM_GETTEXTEX, textW);
3116             expect_textA(hwnd, EM_GETTEXTEX, textA);
3117         }
3118     }
3119     DestroyWindow(hwnd);
3120
3121     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
3122                            0, 0, 200, 60, 0, 0, 0, 0);
3123     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3124
3125     ret = IsWindowUnicode(hwnd);
3126     ok(!ret, "RichEdit20A should NOT be unicode\n");
3127
3128     set_textA(hwnd, WM_SETTEXT, textA);
3129     expect_textA(hwnd, WM_GETTEXT, textA);
3130     expect_textA(hwnd, EM_GETTEXTEX, textA);
3131     expect_textW(hwnd, EM_GETTEXTEX, textW);
3132
3133     if (em_settextex_supported)
3134     {
3135         set_textA(hwnd, EM_SETTEXTEX, textA);
3136         expect_textA(hwnd, WM_GETTEXT, textA);
3137         expect_textA(hwnd, EM_GETTEXTEX, textA);
3138         expect_textW(hwnd, EM_GETTEXTEX, textW);
3139     }
3140
3141     if (!is_win9x)
3142     {
3143         set_textW(hwnd, WM_SETTEXT, textW);
3144         expect_textW(hwnd, WM_GETTEXT, textW);
3145         expect_textA(hwnd, WM_GETTEXT, textA);
3146         expect_textW(hwnd, EM_GETTEXTEX, textW);
3147         expect_textA(hwnd, EM_GETTEXTEX, textA);
3148
3149         if (em_settextex_supported)
3150         {
3151             set_textW(hwnd, EM_SETTEXTEX, textW);
3152             expect_textW(hwnd, WM_GETTEXT, textW);
3153             expect_textA(hwnd, WM_GETTEXT, textA);
3154             expect_textW(hwnd, EM_GETTEXTEX, textW);
3155             expect_textA(hwnd, EM_GETTEXTEX, textA);
3156         }
3157     }
3158     DestroyWindow(hwnd);
3159 }
3160
3161 static void test_WM_CHAR(void)
3162 {
3163     HWND hwnd;
3164     int ret;
3165     const char * char_list = "abc\rabc\r";
3166     const char * expected_content_single = "abcabc";
3167     const char * expected_content_multi = "abc\r\nabc\r\n";
3168     char buffer[64] = {0};
3169     const char * p;
3170
3171     /* single-line control must IGNORE carriage returns */
3172     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3173                            0, 0, 200, 60, 0, 0, 0, 0);
3174     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3175
3176     p = char_list;
3177     while (*p != '\0') {
3178         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3179         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3180         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3181         SendMessageA(hwnd, WM_KEYUP, *p, 1);
3182         p++;
3183     }
3184
3185     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3186     ret = strcmp(buffer, expected_content_single);
3187     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3188
3189     DestroyWindow(hwnd);
3190
3191     /* multi-line control inserts CR normally */
3192     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
3193                            0, 0, 200, 60, 0, 0, 0, 0);
3194     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3195
3196     p = char_list;
3197     while (*p != '\0') {
3198         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3199         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3200         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3201         SendMessageA(hwnd, WM_KEYUP, *p, 1);
3202         p++;
3203     }
3204
3205     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3206     ret = strcmp(buffer, expected_content_multi);
3207     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3208
3209     DestroyWindow(hwnd);
3210 }
3211
3212 static void test_EM_GETTEXTLENGTHEX(void)
3213 {
3214     HWND hwnd;
3215     GETTEXTLENGTHEX gtl;
3216     int ret;
3217     const char * base_string = "base string";
3218     const char * test_string = "a\nb\n\n\r\n";
3219     const char * test_string_after = "a";
3220     const char * test_string_2 = "a\rtest\rstring";
3221     char buffer[64] = {0};
3222
3223     /* single line */
3224     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3225                            0, 0, 200, 60, 0, 0, 0, 0);
3226     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3227
3228     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3229     gtl.codepage = CP_ACP;
3230     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3231     ok(ret == 0, "ret %d\n",ret);
3232
3233     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3234     gtl.codepage = CP_ACP;
3235     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3236     ok(ret == 0, "ret %d\n",ret);
3237
3238     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3239
3240     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3241     gtl.codepage = CP_ACP;
3242     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3243     ok(ret == strlen(base_string), "ret %d\n",ret);
3244
3245     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3246     gtl.codepage = CP_ACP;
3247     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3248     ok(ret == strlen(base_string), "ret %d\n",ret);
3249
3250     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3251
3252     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3253     gtl.codepage = CP_ACP;
3254     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3255     ok(ret == 1, "ret %d\n",ret);
3256
3257     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3258     gtl.codepage = CP_ACP;
3259     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3260     ok(ret == 1, "ret %d\n",ret);
3261
3262     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3263     ret = strcmp(buffer, test_string_after);
3264     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3265
3266     DestroyWindow(hwnd);
3267
3268     /* multi line */
3269     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3270                            0, 0, 200, 60, 0, 0, 0, 0);
3271     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3272
3273     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3274     gtl.codepage = CP_ACP;
3275     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3276     ok(ret == 0, "ret %d\n",ret);
3277
3278     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3279     gtl.codepage = CP_ACP;
3280     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3281     ok(ret == 0, "ret %d\n",ret);
3282
3283     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3284
3285     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3286     gtl.codepage = CP_ACP;
3287     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3288     ok(ret == strlen(base_string), "ret %d\n",ret);
3289
3290     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3291     gtl.codepage = CP_ACP;
3292     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3293     ok(ret == strlen(base_string), "ret %d\n",ret);
3294
3295     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3296
3297     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3298     gtl.codepage = CP_ACP;
3299     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3300     ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3301
3302     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3303     gtl.codepage = CP_ACP;
3304     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3305     ok(ret == strlen(test_string_2), "ret %d\n",ret);
3306
3307     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3308
3309     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3310     gtl.codepage = CP_ACP;
3311     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3312     ok(ret == 10, "ret %d\n",ret);
3313
3314     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3315     gtl.codepage = CP_ACP;
3316     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
3317     ok(ret == 6, "ret %d\n",ret);
3318
3319     DestroyWindow(hwnd);
3320 }
3321
3322
3323 /* globals that parent and child access when checking event masks & notifications */
3324 static HWND eventMaskEditHwnd = 0;
3325 static int queriedEventMask;
3326 static int watchForEventMask = 0;
3327
3328 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3329 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3330 {
3331     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3332     {
3333       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3334     }
3335     return DefWindowProcA(hwnd, message, wParam, lParam);
3336 }
3337
3338 /* test event masks in combination with WM_COMMAND */
3339 static void test_eventMask(void)
3340 {
3341     HWND parent;
3342     int ret;
3343     WNDCLASSA cls;
3344     const char text[] = "foo bar\n";
3345     int eventMask;
3346
3347     /* register class to capture WM_COMMAND */
3348     cls.style = 0;
3349     cls.lpfnWndProc = ParentMsgCheckProcA;
3350     cls.cbClsExtra = 0;
3351     cls.cbWndExtra = 0;
3352     cls.hInstance = GetModuleHandleA(0);
3353     cls.hIcon = 0;
3354     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3355     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3356     cls.lpszMenuName = NULL;
3357     cls.lpszClassName = "EventMaskParentClass";
3358     if(!RegisterClassA(&cls)) assert(0);
3359
3360     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3361                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3362     ok (parent != 0, "Failed to create parent window\n");
3363
3364     eventMaskEditHwnd = new_richedit(parent);
3365     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3366
3367     eventMask = ENM_CHANGE | ENM_UPDATE;
3368     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3369     ok(ret == ENM_NONE, "wrong event mask\n");
3370     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3371     ok(ret == eventMask, "failed to set event mask\n");
3372
3373     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3374     queriedEventMask = 0;  /* initialize to something other than we expect */
3375     watchForEventMask = EN_CHANGE;
3376     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3377     ok(ret == TRUE, "failed to set text\n");
3378     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3379        notification in response to WM_SETTEXT */
3380     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3381             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3382
3383 }
3384
3385 static int received_WM_NOTIFY = 0;
3386 static int modify_at_WM_NOTIFY = 0;
3387 static HWND hwndRichedit_WM_NOTIFY;
3388
3389 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3390 {
3391     if(message == WM_NOTIFY)
3392     {
3393       received_WM_NOTIFY = 1;
3394       modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3395     }
3396     return DefWindowProcA(hwnd, message, wParam, lParam);
3397 }
3398
3399 static void test_WM_NOTIFY(void)
3400 {
3401     HWND parent;
3402     WNDCLASSA cls;
3403     CHARFORMAT2 cf2;
3404
3405     /* register class to capture WM_NOTIFY */
3406     cls.style = 0;
3407     cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3408     cls.cbClsExtra = 0;
3409     cls.cbWndExtra = 0;
3410     cls.hInstance = GetModuleHandleA(0);
3411     cls.hIcon = 0;
3412     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3413     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3414     cls.lpszMenuName = NULL;
3415     cls.lpszClassName = "WM_NOTIFY_ParentClass";
3416     if(!RegisterClassA(&cls)) assert(0);
3417
3418     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3419                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
3420     ok (parent != 0, "Failed to create parent window\n");
3421
3422     hwndRichedit_WM_NOTIFY = new_richedit(parent);
3423     ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3424
3425     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3426
3427     /* Notifications for selection change should only be sent when selection
3428        actually changes. EM_SETCHARFORMAT is one message that calls
3429        ME_CommitUndo, which should check whether message should be sent */
3430     received_WM_NOTIFY = 0;
3431     cf2.cbSize = sizeof(CHARFORMAT2);
3432     SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3433              (LPARAM) &cf2);
3434     cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3435     cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3436     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3437     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3438
3439     /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3440        already at 0. */
3441     received_WM_NOTIFY = 0;
3442     modify_at_WM_NOTIFY = 0;
3443     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3444     ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3445     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3446
3447     received_WM_NOTIFY = 0;
3448     modify_at_WM_NOTIFY = 0;
3449     SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3450     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3451
3452     received_WM_NOTIFY = 0;
3453     modify_at_WM_NOTIFY = 0;
3454     SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3455     ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3456     ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3457
3458     DestroyWindow(hwndRichedit_WM_NOTIFY);
3459     DestroyWindow(parent);
3460 }
3461
3462 START_TEST( editor )
3463 {
3464   MSG msg;
3465   time_t end;
3466
3467   /* Must explicitly LoadLibrary(). The test has no references to functions in
3468    * RICHED20.DLL, so the linker doesn't actually link to it. */
3469   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3470   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3471   test_WM_CHAR();
3472   test_EM_FINDTEXT();
3473   test_EM_GETLINE();
3474   test_EM_SCROLLCARET();
3475   test_EM_SCROLL();
3476   test_WM_SETTEXT();
3477   test_EM_LINELENGTH();
3478   test_EM_SETCHARFORMAT();
3479   test_EM_SETTEXTMODE();
3480   test_TM_PLAINTEXT();
3481   test_EM_SETOPTIONS();
3482   test_WM_GETTEXT();
3483   test_EM_GETTEXTRANGE();
3484   test_EM_GETSELTEXT();
3485   test_EM_SETUNDOLIMIT();
3486   test_ES_PASSWORD();
3487   test_EM_SETTEXTEX();
3488   test_EM_LIMITTEXT();
3489   test_EM_EXLIMITTEXT();
3490   test_EM_GETLIMITTEXT();
3491   test_WM_SETFONT();
3492   test_EM_GETMODIFY();
3493   test_EM_EXSETSEL();
3494   test_WM_PASTE();
3495   test_EM_AUTOURLDETECT();
3496   test_EM_STREAMIN();
3497   test_EM_STREAMOUT();
3498   test_EM_StreamIn_Undo();
3499   test_EM_FORMATRANGE();
3500   test_unicode_conversions();
3501   test_EM_GETTEXTLENGTHEX();
3502   test_EM_REPLACESEL(1);
3503   test_EM_REPLACESEL(0);
3504   test_WM_NOTIFY();
3505   test_eventMask();
3506
3507   /* Set the environment variable WINETEST_RICHED20 to keep windows
3508    * responsive and open for 30 seconds. This is useful for debugging.
3509    *
3510    * The message pump uses PeekMessage() to empty the queue and then sleeps for
3511    * 50ms before retrying the queue. */
3512   end = time(NULL) + 30;
3513   if (getenv( "WINETEST_RICHED20" )) {
3514     while (time(NULL) < end) {
3515       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3516         TranslateMessage(&msg);
3517         DispatchMessage(&msg);
3518       } else {
3519         Sleep(50);
3520       }
3521     }
3522   }
3523
3524   OleFlushClipboard();
3525   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
3526 }