mshtml: Added IHTMLStyleSheet::get_rules implementation.
[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
133 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
134   int findloc;
135   FINDTEXT ft;
136   memset(&ft, 0, sizeof(ft));
137   ft.chrg.cpMin = f->start;
138   ft.chrg.cpMax = f->end;
139   ft.lpstrText = f->needle;
140   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
141   ok(findloc == f->expected_loc,
142      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
143      name, id, f->needle, f->start, f->end, f->flags, findloc);
144 }
145
146 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
147     int id) {
148   int findloc;
149   FINDTEXTEX ft;
150   memset(&ft, 0, sizeof(ft));
151   ft.chrg.cpMin = f->start;
152   ft.chrg.cpMax = f->end;
153   ft.lpstrText = f->needle;
154   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
155   ok(findloc == f->expected_loc,
156       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
157       name, id, f->needle, f->start, f->end, f->flags, findloc);
158   ok(ft.chrgText.cpMin == f->expected_loc,
159       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
160       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
161   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
162         : f->expected_loc + strlen(f->needle)),
163       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
164       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
165 }
166
167 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
168     int num_tests)
169 {
170   int i;
171
172   for (i = 0; i < num_tests; i++) {
173     if (find[i]._todo_wine) {
174       todo_wine {
175         check_EM_FINDTEXT(hwnd, name, &find[i], i);
176         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
177       }
178     } else {
179         check_EM_FINDTEXT(hwnd, name, &find[i], i);
180         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
181     }
182   }
183 }
184
185 static void test_EM_FINDTEXT(void)
186 {
187   HWND hwndRichEdit = new_richedit(NULL);
188
189   /* Empty rich edit control */
190   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
191       sizeof(find_tests)/sizeof(struct find_s));
192
193   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
194
195   /* Haystack text */
196   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
197       sizeof(find_tests2)/sizeof(struct find_s));
198
199   DestroyWindow(hwndRichEdit);
200 }
201
202 static const struct getline_s {
203   int line;
204   size_t buffer_len;
205   const char *text;
206 } gl[] = {
207   {0, 10, "foo bar\r"},
208   {1, 10, "\r"},
209   {2, 10, "bar\r"},
210   {3, 10, "\r"},
211
212   /* Buffer smaller than line length */
213   {0, 2, "foo bar\r"},
214   {0, 1, "foo bar\r"},
215   {0, 0, "foo bar\r"}
216 };
217
218 static void test_EM_GETLINE(void)
219 {
220   int i;
221   HWND hwndRichEdit = new_richedit(NULL);
222   static const int nBuf = 1024;
223   char dest[1024], origdest[1024];
224   const char text[] = "foo bar\n"
225       "\n"
226       "bar\n";
227
228   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
229
230   memset(origdest, 0xBB, nBuf);
231   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
232   {
233     int nCopied;
234     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
235     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
236     memset(dest, 0xBB, nBuf);
237     *(WORD *) dest = gl[i].buffer_len;
238
239     /* EM_GETLINE appends a "\r\0" to the end of the line
240      * nCopied counts up to and including the '\r' */
241     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
242     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
243        expected_nCopied);
244     /* two special cases since a parameter is passed via dest */
245     if (gl[i].buffer_len == 0)
246       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
247          "buffer_len=0\n");
248     else if (gl[i].buffer_len == 1)
249       ok(dest[0] == gl[i].text[0] && !dest[1] &&
250          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
251     else
252     {
253       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
254          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
255       ok(!strncmp(dest + expected_bytes_written, origdest
256                   + expected_bytes_written, nBuf - expected_bytes_written),
257          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
258     }
259   }
260
261   DestroyWindow(hwndRichEdit);
262 }
263
264 static int get_scroll_pos_y(HWND hwnd)
265 {
266   POINT p = {-1, -1};
267   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
268   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
269   return p.y;
270 }
271
272 static void move_cursor(HWND hwnd, long charindex)
273 {
274   CHARRANGE cr;
275   cr.cpMax = charindex;
276   cr.cpMin = charindex;
277   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
278 }
279
280 static void line_scroll(HWND hwnd, int amount)
281 {
282   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
283 }
284
285 static void test_EM_SCROLLCARET(void)
286 {
287   int prevY, curY;
288   HWND hwndRichEdit = new_richedit(NULL);
289   const char text[] = "aa\n"
290       "this is a long line of text that should be longer than the "
291       "control's width\n"
292       "cc\n"
293       "dd\n"
294       "ee\n"
295       "ff\n"
296       "gg\n"
297       "hh\n";
298
299   /* Can't verify this */
300   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
301
302   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
303
304   /* Caret above visible window */
305   line_scroll(hwndRichEdit, 3);
306   prevY = get_scroll_pos_y(hwndRichEdit);
307   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
308   curY = get_scroll_pos_y(hwndRichEdit);
309   ok(prevY != curY, "%d == %d\n", prevY, curY);
310
311   /* Caret below visible window */
312   move_cursor(hwndRichEdit, sizeof(text) - 1);
313   line_scroll(hwndRichEdit, -3);
314   prevY = get_scroll_pos_y(hwndRichEdit);
315   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
316   curY = get_scroll_pos_y(hwndRichEdit);
317   ok(prevY != curY, "%d == %d\n", prevY, curY);
318
319   /* Caret in visible window */
320   move_cursor(hwndRichEdit, sizeof(text) - 2);
321   prevY = get_scroll_pos_y(hwndRichEdit);
322   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
323   curY = get_scroll_pos_y(hwndRichEdit);
324   ok(prevY == curY, "%d != %d\n", prevY, curY);
325
326   /* Caret still in visible window */
327   line_scroll(hwndRichEdit, -1);
328   prevY = get_scroll_pos_y(hwndRichEdit);
329   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
330   curY = get_scroll_pos_y(hwndRichEdit);
331   ok(prevY == curY, "%d != %d\n", prevY, curY);
332
333   DestroyWindow(hwndRichEdit);
334 }
335
336 static void test_EM_SETCHARFORMAT(void)
337 {
338   HWND hwndRichEdit = new_richedit(NULL);
339   CHARFORMAT2 cf2;
340   int rc = 0;
341
342   /* Invalid flags, CHARFORMAT2 structure blanked out */
343   memset(&cf2, 0, sizeof(cf2));
344   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
345              (LPARAM) &cf2);
346   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
347
348   /* A valid flag, CHARFORMAT2 structure blanked out */
349   memset(&cf2, 0, sizeof(cf2));
350   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
351              (LPARAM) &cf2);
352   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
353
354   /* A valid flag, CHARFORMAT2 structure blanked out */
355   memset(&cf2, 0, sizeof(cf2));
356   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
357              (LPARAM) &cf2);
358   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
359
360   /* A valid flag, CHARFORMAT2 structure blanked out */
361   memset(&cf2, 0, sizeof(cf2));
362   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
363              (LPARAM) &cf2);
364   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
365
366   /* A valid flag, CHARFORMAT2 structure blanked out */
367   memset(&cf2, 0, sizeof(cf2));
368   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
369              (LPARAM) &cf2);
370   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
371
372   /* Invalid flags, CHARFORMAT2 structure minimally filled */
373   memset(&cf2, 0, sizeof(cf2));
374   cf2.cbSize = sizeof(CHARFORMAT2);
375   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
376              (LPARAM) &cf2);
377   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
378
379   /* A valid flag, CHARFORMAT2 structure minimally filled */
380   memset(&cf2, 0, sizeof(cf2));
381   cf2.cbSize = sizeof(CHARFORMAT2);
382   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
383              (LPARAM) &cf2);
384   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
385
386   /* A valid flag, CHARFORMAT2 structure minimally filled */
387   memset(&cf2, 0, sizeof(cf2));
388   cf2.cbSize = sizeof(CHARFORMAT2);
389   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
390              (LPARAM) &cf2);
391   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
392
393   /* A valid flag, CHARFORMAT2 structure minimally filled */
394   memset(&cf2, 0, sizeof(cf2));
395   cf2.cbSize = sizeof(CHARFORMAT2);
396   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
397              (LPARAM) &cf2);
398   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
399
400   /* A valid flag, CHARFORMAT2 structure minimally filled */
401   memset(&cf2, 0, sizeof(cf2));
402   cf2.cbSize = sizeof(CHARFORMAT2);
403   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
404              (LPARAM) &cf2);
405   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
406
407   DestroyWindow(hwndRichEdit);
408 }
409
410 static void test_EM_SETTEXTMODE(void)
411 {
412   HWND hwndRichEdit = new_richedit(NULL);
413   CHARFORMAT2 cf2, cf2test;
414   CHARRANGE cr;
415   int rc = 0;
416
417   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
418   /*Insert text into the control*/
419
420   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
421
422   /*Attempt to change the control to plain text mode*/
423   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
424   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
425
426   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
427   If rich text is pasted, it should have the same formatting as the rest
428   of the text in the control*/
429
430   /*Italicize the text
431   *NOTE: If the default text was already italicized, the test will simply
432   reverse; in other words, it will copy a regular "wine" into a plain
433   text window that uses an italicized format*/
434   cf2.cbSize = sizeof(CHARFORMAT2);
435   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
436              (LPARAM) &cf2);
437
438   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
439   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
440
441   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
442   however, SCF_ALL has been implemented*/
443   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
444   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
445   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
446
447   /*Select the string "wine"*/
448   cr.cpMin = 0;
449   cr.cpMax = 4;
450   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
451
452   /*Copy the italicized "wine" to the clipboard*/
453   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
454
455   /*Reset the formatting to default*/
456   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
457   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
458   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
459
460   /*Clear the text in the control*/
461   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
462
463   /*Switch to Plain Text Mode*/
464   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
465   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
466
467   /*Input "wine" again in normal format*/
468   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
469
470   /*Paste the italicized "wine" into the control*/
471   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
472
473   /*Select a character from the first "wine" string*/
474   cr.cpMin = 2;
475   cr.cpMax = 3;
476   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
477
478   /*Retrieve its formatting*/
479   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
480               (LPARAM) &cf2);
481
482   /*Select a character from the second "wine" string*/
483   cr.cpMin = 5;
484   cr.cpMax = 6;
485   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
486
487   /*Retrieve its formatting*/
488   cf2test.cbSize = sizeof(CHARFORMAT2);
489   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
490                (LPARAM) &cf2test);
491
492   /*Compare the two formattings*/
493     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
494       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
495        cf2.dwEffects, cf2test.dwEffects);
496   /*Test TM_RICHTEXT by: switching back to Rich Text mode
497                          printing "wine" in the current format(normal)
498                          pasting "wine" from the clipboard(italicized)
499                          comparing the two formats(should differ)*/
500
501   /*Attempt to switch with text in control*/
502   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
503   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
504
505   /*Clear control*/
506   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
507
508   /*Switch into Rich Text mode*/
509   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
510   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
511
512   /*Print "wine" in normal formatting into the control*/
513   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
514
515   /*Paste italicized "wine" into the control*/
516   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
517
518   /*Select text from the first "wine" string*/
519   cr.cpMin = 1;
520   cr.cpMax = 3;
521   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
522
523   /*Retrieve its formatting*/
524   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
525                 (LPARAM) &cf2);
526
527   /*Select text from the second "wine" string*/
528   cr.cpMin = 6;
529   cr.cpMax = 7;
530   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
531
532   /*Retrieve its formatting*/
533   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
534                 (LPARAM) &cf2test);
535
536   /*Test that the two formattings are not the same*/
537   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
538       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
539       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
540
541   DestroyWindow(hwndRichEdit);
542 }
543
544 static void test_TM_PLAINTEXT(void)
545 {
546   /*Tests plain text properties*/
547
548   HWND hwndRichEdit = new_richedit(NULL);
549   CHARFORMAT2 cf2, cf2test;
550   CHARRANGE cr;
551   int rc = 0;
552
553   /*Switch to plain text mode*/
554
555   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
556   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
557
558   /*Fill control with text*/
559
560   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
561
562   /*Select some text and bold it*/
563
564   cr.cpMin = 10;
565   cr.cpMax = 20;
566   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
567   cf2.cbSize = sizeof(CHARFORMAT2);
568   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
569               (LPARAM) &cf2);
570
571   cf2.dwMask = CFM_BOLD | cf2.dwMask;
572   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
573
574   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
575   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
576
577   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
578   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
579
580   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
581   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
582
583   /*Get the formatting of those characters*/
584
585   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
586
587   /*Get the formatting of some other characters*/
588   cf2test.cbSize = sizeof(CHARFORMAT2);
589   cr.cpMin = 21;
590   cr.cpMax = 30;
591   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
592   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
593
594   /*Test that they are the same as plain text allows only one formatting*/
595
596   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
597      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
598      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
599   
600   /*Fill the control with a "wine" string, which when inserted will be bold*/
601
602   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
603
604   /*Copy the bolded "wine" string*/
605
606   cr.cpMin = 0;
607   cr.cpMax = 4;
608   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
609   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
610
611   /*Swap back to rich text*/
612
613   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
614   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
615
616   /*Set the default formatting to bold italics*/
617
618   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
619   cf2.dwMask |= CFM_ITALIC;
620   cf2.dwEffects ^= CFE_ITALIC;
621   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
622   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
623
624   /*Set the text in the control to "wine", which will be bold and italicized*/
625
626   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
627
628   /*Paste the plain text "wine" string, which should take the insert
629    formatting, which at the moment is bold italics*/
630
631   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
632
633   /*Select the first "wine" string and retrieve its formatting*/
634
635   cr.cpMin = 1;
636   cr.cpMax = 3;
637   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
638   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
639
640   /*Select the second "wine" string and retrieve its formatting*/
641
642   cr.cpMin = 5;
643   cr.cpMax = 7;
644   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
645   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
646
647   /*Compare the two formattings. They should be the same.*/
648
649   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
650      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
651      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
652   DestroyWindow(hwndRichEdit);
653 }
654
655 static void test_WM_GETTEXT(void)
656 {
657     HWND hwndRichEdit = new_richedit(NULL);
658     static const char text[] = "Hello. My name is RichEdit!";
659     char buffer[1024] = {0};
660     int result;
661
662     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
663     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
664     result = strcmp(buffer,text);
665     ok(result == 0, 
666         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
667     DestroyWindow(hwndRichEdit);
668 }
669
670 /* FIXME: need to test unimplemented options and robustly test wparam */
671 static void test_EM_SETOPTIONS(void)
672 {
673     HWND hwndRichEdit = new_richedit(NULL);
674     static const char text[] = "Hello. My name is RichEdit!";
675     char buffer[1024] = {0};
676
677     /* NEGATIVE TESTING - NO OPTIONS SET */
678     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
679     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
680
681     /* testing no readonly by sending 'a' to the control*/
682     SetFocus(hwndRichEdit);
683     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
684     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
685     ok(buffer[0]=='a', 
686        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
687     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
688
689     /* READONLY - sending 'a' to the control */
690     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
691     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
692     SetFocus(hwndRichEdit);
693     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
694     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
695     ok(buffer[0]==text[0], 
696        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
697
698     DestroyWindow(hwndRichEdit);
699 }
700
701 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
702 {
703   CHARFORMAT2W text_format;
704   int link_present = 0;
705   text_format.cbSize = sizeof(text_format);
706   SendMessage(hwnd, EM_SETSEL, 0, 1);
707   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
708   link_present = text_format.dwEffects & CFE_LINK;
709   if (is_url) 
710   { /* control text is url; should get CFE_LINK */
711         ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
712   }
713   else 
714   {
715     ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
716   }
717 }
718
719 static HWND new_static_wnd(HWND parent) {
720   return new_window("Static", 0, parent);
721 }
722
723 static void test_EM_AUTOURLDETECT(void)
724 {
725   struct urls_s {
726     const char *text;
727     int is_url;
728   } urls[12] = {
729     {"winehq.org", 0},
730     {"http://www.winehq.org", 1},
731     {"http//winehq.org", 0},
732     {"ww.winehq.org", 0},
733     {"www.winehq.org", 1},
734     {"ftp://192.168.1.1", 1},
735     {"ftp//192.168.1.1", 0},
736     {"mailto:your@email.com", 1},    
737     {"prospero:prosperoserver", 1},
738     {"telnet:test", 1},
739     {"news:newserver", 1},
740     {"wais:waisserver", 1}  
741   };
742
743   int i;
744   int urlRet=-1;
745   HWND hwndRichEdit, parent;
746
747   parent = new_static_wnd(NULL);
748   hwndRichEdit = new_richedit(parent);
749   /* Try and pass EM_AUTOURLDETECT some test wParam values */
750   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
751   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
752   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
753   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
754   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
755   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
756   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
757   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
758   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
759   /* for each url, check the text to see if CFE_LINK effect is present */
760   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
761     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
762     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
763     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
764     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
765     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
766     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
767     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
768     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
769   }
770   DestroyWindow(hwndRichEdit);
771   DestroyWindow(parent);
772 }
773
774 static void test_EM_SCROLL(void)
775 {
776   int i, j;
777   int r; /* return value */
778   int expr; /* expected return value */
779   HWND hwndRichEdit = new_richedit(NULL);
780   int y_before, y_after; /* units of lines of text */
781
782   /* test a richedit box containing a single line of text */
783   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
784   expr = 0x00010000;
785   for (i = 0; i < 4; i++) {
786     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
787
788     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
789     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
790     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
791        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
792     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
793        "(i == %d)\n", y_after, i);
794   }
795
796   /*
797    * test a richedit box that will scroll. There are two general
798    * cases: the case without any long lines and the case with a long
799    * line.
800    */
801   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
802     if (i == 0)
803       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
804     else
805       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
806                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
807                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
808                   "LONG LINE \nb\nc\nd\ne");
809     for (j = 0; j < 12; j++) /* reset scrol position to top */
810       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
811
812     /* get first visible line */
813     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
814     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
815
816     /* get new current first visible line */
817     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
818
819     ok(((r & 0xffffff00) == 0x00010000) &&
820        ((r & 0x000000ff) != 0x00000000),
821        "EM_SCROLL page down didn't scroll by a small positive number of "
822        "lines (r == 0x%08x)\n", r);
823     ok(y_after > y_before, "EM_SCROLL page down not functioning "
824        "(line %d scrolled to line %d\n", y_before, y_after);
825
826     y_before = y_after;
827     
828     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
829     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
830     ok(((r & 0xffffff00) == 0x0001ff00),
831        "EM_SCROLL page up didn't scroll by a small negative number of lines "
832        "(r == 0x%08x)\n", r);
833     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
834        "%d scrolled to line %d\n", y_before, y_after);
835     
836     y_before = y_after;
837
838     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
839
840     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
841
842     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
843        "(r == 0x%08x)\n", r);
844     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
845        "1 line (%d scrolled to %d)\n", y_before, y_after);
846
847     y_before = y_after;
848
849     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
850
851     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
852
853     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
854        "(r == 0x%08x)\n", r);
855     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
856        "line (%d scrolled to %d)\n", y_before, y_after);
857
858     y_before = y_after;
859
860     r = SendMessage(hwndRichEdit, EM_SCROLL,
861                     SB_LINEUP, 0); /* lineup beyond top */
862
863     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
864
865     ok(r == 0x00010000,
866        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
867     ok(y_before == y_after,
868        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
869
870     y_before = y_after;
871
872     r = SendMessage(hwndRichEdit, EM_SCROLL,
873                     SB_PAGEUP, 0);/*page up beyond top */
874
875     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
876
877     ok(r == 0x00010000,
878        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
879     ok(y_before == y_after,
880        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
881
882     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
883       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
884     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
885     r = SendMessage(hwndRichEdit, EM_SCROLL,
886                     SB_PAGEDOWN, 0); /* page down beyond bot */
887     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
888
889     ok(r == 0x00010000,
890        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
891     ok(y_before == y_after,
892        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
893        y_before, y_after);
894
895     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
896     SendMessage(hwndRichEdit, EM_SCROLL,
897                 SB_LINEDOWN, 0); /* line down beyond bot */
898     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
899     
900     ok(r == 0x00010000,
901        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
902     ok(y_before == y_after,
903        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
904        y_before, y_after);
905   }
906   DestroyWindow(hwndRichEdit);
907 }
908
909 static void test_EM_SETUNDOLIMIT(void)
910 {
911   /* cases we test for:
912    * default behaviour - limiting at 100 undo's 
913    * undo disabled - setting a limit of 0
914    * undo limited -  undo limit set to some to some number, like 2
915    * bad input - sending a negative number should default to 100 undo's */
916  
917   HWND hwndRichEdit = new_richedit(NULL);
918   CHARRANGE cr;
919   int i;
920   int result;
921   
922   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
923   cr.cpMin = 0;
924   cr.cpMax = 1;
925   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
926     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
927       also, multiple pastes don't combine like WM_CHAR would */
928   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
929
930   /* first case - check the default */
931   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
932   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
933     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
934   for (i=0; i<100; i++) /* Undo 100 of them */
935     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
936   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
937      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
938
939   /* second case - cannot undo */
940   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
941   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
942   SendMessage(hwndRichEdit,
943               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
944   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
945      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
946
947   /* third case - set it to an arbitrary number */
948   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
949   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
950   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
951   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
952   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
953   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
954   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
955      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
956   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
957   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
958      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
959   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
960   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
961      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
962   
963   /* fourth case - setting negative numbers should default to 100 undos */
964   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
965   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
966   ok (result == 100, 
967       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
968       
969   DestroyWindow(hwndRichEdit);
970 }
971
972 static void test_ES_PASSWORD(void)
973 {
974   /* This isn't hugely testable, so we're just going to run it through its paces */
975
976   HWND hwndRichEdit = new_richedit(NULL);
977   WCHAR result;
978
979   /* First, check the default of a regular control */
980   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
981   ok (result == 0,
982         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
983
984   /* Now, set it to something normal */
985   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
986   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
987   ok (result == 120,
988         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
989
990   /* Now, set it to something odd */
991   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
992   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
993   ok (result == 1234,
994         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
995   DestroyWindow(hwndRichEdit);
996 }
997
998 static void test_WM_SETTEXT()
999 {
1000   HWND hwndRichEdit = new_richedit(NULL);
1001   const char * TestItem1 = "TestSomeText";
1002   const char * TestItem2 = "TestSomeText\r";
1003   const char * TestItem2_after = "TestSomeText\r\n";
1004   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1005   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1006   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1007   const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1008   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1009   const char * TestItem5_after = "TestSomeText TestSomeText";
1010   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1011   const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1012   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1013   const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1014   char buf[1024] = {0};
1015   LRESULT result;
1016
1017   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1018      any solitary \r to be converted to \r\n on return. Properly paired
1019      \r\n are not affected. It also shows that the special sequence \r\r\n
1020      gets converted to a single space.
1021    */
1022
1023 #define TEST_SETTEXT(a, b) \
1024   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1025   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1026   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1027   ok (result == strlen(buf), \
1028         "WM_GETTEXT returned %ld instead of expected %u\n", \
1029         result, strlen(buf)); \
1030   result = strcmp(b, buf); \
1031   ok(result == 0, \
1032         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1033
1034   TEST_SETTEXT(TestItem1, TestItem1)
1035   TEST_SETTEXT(TestItem2, TestItem2_after)
1036   TEST_SETTEXT(TestItem3, TestItem3_after)
1037   TEST_SETTEXT(TestItem3_after, TestItem3_after)
1038   TEST_SETTEXT(TestItem4, TestItem4_after)
1039   TEST_SETTEXT(TestItem5, TestItem5_after)
1040   TEST_SETTEXT(TestItem6, TestItem6_after)
1041   TEST_SETTEXT(TestItem7, TestItem7_after)
1042
1043 #undef TEST_SETTEXT
1044   DestroyWindow(hwndRichEdit);
1045 }
1046
1047 static void test_EM_SETTEXTEX(void)
1048 {
1049   HWND hwndRichEdit = new_richedit(NULL);
1050   SETTEXTEX setText;
1051   GETTEXTEX getText;
1052   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1053                        'S', 'o', 'm', 'e', 
1054                        'T', 'e', 'x', 't', 0}; 
1055   WCHAR TestItem2[] = {'T', 'e', 's', 't',
1056                        'S', 'o', 'm', 'e',
1057                        'T', 'e', 'x', 't',
1058                       '\r', 0};
1059   const char * TestItem2_after = "TestSomeText\r\n";
1060   WCHAR TestItem3[] = {'T', 'e', 's', 't',
1061                        'S', 'o', 'm', 'e',
1062                        'T', 'e', 'x', 't',
1063                       '\r','\n','\r','\n', 0};
1064   WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1065                        'S', 'o', 'm', 'e',
1066                        'T', 'e', 'x', 't',
1067                        '\n','\n', 0};
1068   WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1069                        'S', 'o', 'm', 'e',
1070                        'T', 'e', 'x', 't',
1071                        '\r','\r', 0};
1072   WCHAR TestItem4[] = {'T', 'e', 's', 't',
1073                        'S', 'o', 'm', 'e',
1074                        'T', 'e', 'x', 't',
1075                       '\r','\r','\n','\r',
1076                       '\n', 0};
1077   WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1078                        'S', 'o', 'm', 'e',
1079                        'T', 'e', 'x', 't',
1080                        ' ','\r', 0};
1081 #define MAX_BUF_LEN 1024
1082   WCHAR buf[MAX_BUF_LEN];
1083   int result;
1084   CHARRANGE cr;
1085
1086   setText.codepage = 1200;  /* no constant for unicode */
1087   getText.codepage = 1200;  /* no constant for unicode */
1088   getText.cb = MAX_BUF_LEN;
1089   getText.flags = GT_DEFAULT;
1090   getText.lpDefaultChar = NULL;
1091   getText.lpUsedDefaultChar = NULL;
1092
1093   setText.flags = 0;
1094   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1095   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1096   ok(lstrcmpW(buf, TestItem1) == 0,
1097       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1098
1099   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1100      convert \r to \r\n on return
1101    */
1102   setText.codepage = 1200;  /* no constant for unicode */
1103   getText.codepage = 1200;  /* no constant for unicode */
1104   getText.cb = MAX_BUF_LEN;
1105   getText.flags = GT_DEFAULT;
1106   getText.lpDefaultChar = NULL;
1107   getText.lpUsedDefaultChar = NULL;
1108   setText.flags = 0;
1109   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1110   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1111   ok(lstrcmpW(buf, TestItem2) == 0,
1112       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1113
1114   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1115   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1116   ok(strcmp((const char *)buf, TestItem2_after) == 0,
1117       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1118
1119   /* \r\n pairs get changed into \r */
1120   setText.codepage = 1200;  /* no constant for unicode */
1121   getText.codepage = 1200;  /* no constant for unicode */
1122   getText.cb = MAX_BUF_LEN;
1123   getText.flags = GT_DEFAULT;
1124   getText.lpDefaultChar = NULL;
1125   getText.lpUsedDefaultChar = NULL;
1126   setText.flags = 0;
1127   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1128   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1129   ok(lstrcmpW(buf, TestItem3_after) == 0,
1130       "EM_SETTEXTEX did not convert properly\n");
1131
1132   /* \n also gets changed to \r */
1133   setText.codepage = 1200;  /* no constant for unicode */
1134   getText.codepage = 1200;  /* no constant for unicode */
1135   getText.cb = MAX_BUF_LEN;
1136   getText.flags = GT_DEFAULT;
1137   getText.lpDefaultChar = NULL;
1138   getText.lpUsedDefaultChar = NULL;
1139   setText.flags = 0;
1140   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1141   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1142   ok(lstrcmpW(buf, TestItem3_after) == 0,
1143       "EM_SETTEXTEX did not convert properly\n");
1144
1145   /* \r\r\n gets changed into single space */
1146   setText.codepage = 1200;  /* no constant for unicode */
1147   getText.codepage = 1200;  /* no constant for unicode */
1148   getText.cb = MAX_BUF_LEN;
1149   getText.flags = GT_DEFAULT;
1150   getText.lpDefaultChar = NULL;
1151   getText.lpUsedDefaultChar = NULL;
1152   setText.flags = 0;
1153   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1154   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1155   ok(lstrcmpW(buf, TestItem4_after) == 0,
1156       "EM_SETTEXTEX did not convert properly\n");
1157
1158   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1159                        (WPARAM)&setText, (LPARAM) NULL);
1160   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1161   
1162   ok (result == 1, 
1163       "EM_SETTEXTEX returned %d, instead of 1\n",result);
1164   ok(lstrlenW(buf) == 0,
1165       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1166   
1167   /* put some text back */
1168   setText.flags = 0;
1169   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1170   /* select some text */
1171   cr.cpMax = 1;
1172   cr.cpMin = 3;
1173   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1174   /* replace current selection */
1175   setText.flags = ST_SELECTION;
1176   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1177                        (WPARAM)&setText, (LPARAM) NULL);
1178   ok(result == 0,
1179       "EM_SETTEXTEX with NULL lParam to replace selection"
1180       " with no text should return 0. Got %i\n",
1181       result);
1182   
1183   /* put some text back */
1184   setText.flags = 0;
1185   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1186   /* select some text */
1187   cr.cpMax = 1;
1188   cr.cpMin = 3;
1189   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1190   /* replace current selection */
1191   setText.flags = ST_SELECTION;
1192   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1193                        (WPARAM)&setText, (LPARAM) TestItem1);
1194   /* get text */
1195   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1196   ok(result == lstrlenW(TestItem1),
1197       "EM_SETTEXTEX with NULL lParam to replace selection"
1198       " with no text should return 0. Got %i\n",
1199       result);
1200   ok(lstrlenW(buf) == 22,
1201       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1202       lstrlenW(buf) );
1203
1204   DestroyWindow(hwndRichEdit);
1205 }
1206
1207 static void test_EM_LIMITTEXT(void)
1208 {
1209   int ret;
1210
1211   HWND hwndRichEdit = new_richedit(NULL);
1212
1213   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1214    * about setting the length to -1 for multiline edit controls doesn't happen.
1215    */
1216
1217   /* Don't check default gettextlimit case. That's done in other tests */
1218
1219   /* Set textlimit to 100 */
1220   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1221   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1222   ok (ret == 100,
1223       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1224
1225   /* Set textlimit to 0 */
1226   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1227   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1228   ok (ret == 65536,
1229       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1230
1231   /* Set textlimit to -1 */
1232   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1233   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1234   ok (ret == -1,
1235       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1236
1237   /* Set textlimit to -2 */
1238   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1239   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1240   ok (ret == -2,
1241       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1242
1243   DestroyWindow (hwndRichEdit);
1244 }
1245
1246
1247 static void test_EM_EXLIMITTEXT(void)
1248 {
1249   int i, selBegin, selEnd, len1, len2;
1250   int result;
1251   char text[1024 + 1];
1252   char buffer[1024 + 1];
1253   int textlimit = 0; /* multiple of 100 */
1254   HWND hwndRichEdit = new_richedit(NULL);
1255   
1256   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1257   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1258   
1259   textlimit = 256000;
1260   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1261   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1262   /* set higher */
1263   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1264   
1265   textlimit = 1000;
1266   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1267   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1268   /* set lower */
1269   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1270  
1271   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1272   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1273   /* default for WParam = 0 */
1274   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1275  
1276   textlimit = sizeof(text)-1;
1277   memset(text, 'W', textlimit);
1278   text[sizeof(text)-1] = 0;
1279   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1280   /* maxed out text */
1281   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1282   
1283   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1284   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1285   len1 = selEnd - selBegin;
1286   
1287   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1288   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1289   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1290   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1291   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1292   len2 = selEnd - selBegin;
1293   
1294   ok(len1 != len2,
1295     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1296     len1,len2,i);
1297   
1298   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1299   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1300   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1301   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1302   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1303   len1 = selEnd - selBegin;
1304   
1305   ok(len1 != len2,
1306     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1307     len1,len2,i);
1308   
1309   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1310   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1311   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1312   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1313   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1314   len2 = selEnd - selBegin;
1315   
1316   ok(len1 == len2, 
1317     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1318     len1,len2,i);
1319
1320   /* set text up to the limit, select all the text, then add a char */
1321   textlimit = 5;
1322   memset(text, 'W', textlimit);
1323   text[textlimit] = 0;
1324   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1325   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1326   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1327   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1328   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1329   result = strcmp(buffer, "A");
1330   ok(0 == result, "got string = \"%s\"\n", buffer);
1331
1332   /* WM_SETTEXT not limited */
1333   textlimit = 10;
1334   memset(text, 'W', textlimit);
1335   text[textlimit] = 0;
1336   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1337   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1338   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1339   i = strlen(buffer);
1340   ok(10 == i, "expected 10 chars\n");
1341   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1342   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1343
1344   /* try inserting more text at end */
1345   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1346   ok(0 == i, "WM_CHAR wasn't processed\n");
1347   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1348   i = strlen(buffer);
1349   ok(10 == i, "expected 10 chars, got %i\n", i);
1350   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1351   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1352
1353   /* try inserting text at beginning */
1354   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1355   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1356   ok(0 == i, "WM_CHAR wasn't processed\n");
1357   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1358   i = strlen(buffer);
1359   ok(10 == i, "expected 10 chars, got %i\n", i);
1360   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1361   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1362
1363   /* WM_CHAR is limited */
1364   textlimit = 1;
1365   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1366   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1367   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1368   ok(0 == i, "WM_CHAR wasn't processed\n");
1369   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1370   ok(0 == i, "WM_CHAR wasn't processed\n");
1371   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1372   i = strlen(buffer);
1373   ok(1 == i, "expected 1 chars, got %i instead\n", i);
1374
1375   DestroyWindow(hwndRichEdit);
1376 }
1377
1378 static void test_EM_GETLIMITTEXT(void)
1379 {
1380   int i;
1381   HWND hwndRichEdit = new_richedit(NULL);
1382
1383   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1384   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1385
1386   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1387   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1388   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1389
1390   DestroyWindow(hwndRichEdit);
1391 }
1392
1393 static void test_WM_SETFONT(void)
1394 {
1395   /* There is no invalid input or error conditions for this function.
1396    * NULL wParam and lParam just fall back to their default values 
1397    * It should be noted that even if you use a gibberish name for your fonts
1398    * here, it will still work because the name is stored. They will display as
1399    * System, but will report their name to be whatever they were created as */
1400   
1401   HWND hwndRichEdit = new_richedit(NULL);
1402   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1403     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1404     FF_DONTCARE, "Marlett");
1405   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1406     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1407     FF_DONTCARE, "MS Sans Serif");
1408   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1409     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1410     FF_DONTCARE, "Courier");
1411   LOGFONTA sentLogFont;
1412   CHARFORMAT2A returnedCF2A;
1413   
1414   returnedCF2A.cbSize = sizeof(returnedCF2A);
1415   
1416   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1417   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1418   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1419
1420   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1421   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1422     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1423     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1424
1425   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1426   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1427   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1428   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1429     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1430     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1431     
1432   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1433   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1434   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1435   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1436     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1437     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1438    
1439   /* This last test is special since we send in NULL. We clear the variables
1440    * and just compare to "System" instead of the sent in font name. */
1441   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1442   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1443   returnedCF2A.cbSize = sizeof(returnedCF2A);
1444   
1445   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1446   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1447   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1448   ok (!strcmp("System",returnedCF2A.szFaceName),
1449     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1450   
1451   DestroyWindow(hwndRichEdit);
1452 }
1453
1454
1455 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1456                                          LPBYTE pbBuff,
1457                                          LONG cb,
1458                                          LONG *pcb)
1459 {
1460   const char** str = (const char**)dwCookie;
1461   int size = strlen(*str);
1462   if(size > 3)  /* let's make it peice-meal for fun */
1463     size = 3;
1464   *pcb = cb;
1465   if (*pcb > size) {
1466     *pcb = size;
1467   }
1468   if (*pcb > 0) {
1469     memcpy(pbBuff, *str, *pcb);
1470     *str += *pcb;
1471   }
1472   return 0;
1473 }
1474
1475 static void test_EM_GETMODIFY(void)
1476 {
1477   HWND hwndRichEdit = new_richedit(NULL);
1478   LRESULT result;
1479   SETTEXTEX setText;
1480   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1481                        'S', 'o', 'm', 'e', 
1482                        'T', 'e', 'x', 't', 0}; 
1483   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1484                        'S', 'o', 'm', 'e', 
1485                        'O', 't', 'h', 'e', 'r',
1486                        'T', 'e', 'x', 't', 0}; 
1487   const char* streamText = "hello world";
1488   CHARFORMAT2 cf2;
1489   PARAFORMAT2 pf2;
1490   EDITSTREAM es;
1491   
1492   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1493     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1494     FF_DONTCARE, "Courier");
1495   
1496   setText.codepage = 1200;  /* no constant for unicode */
1497   setText.flags = ST_KEEPUNDO;
1498   
1499
1500   /* modify flag shouldn't be set when richedit is first created */
1501   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1502   ok (result == 0, 
1503       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1504   
1505   /* setting modify flag should actually set it */
1506   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1507   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1508   ok (result != 0, 
1509       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1510   
1511   /* clearing modify flag should actually clear it */
1512   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1513   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1514   ok (result == 0, 
1515       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1516  
1517   /* setting font doesn't change modify flag */
1518   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1519   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1520   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1521   ok (result == 0,
1522       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1523
1524   /* setting text should set modify flag */
1525   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1526   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1527   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1528   ok (result != 0,
1529       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1530   
1531   /* undo previous text doesn't reset modify flag */
1532   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1533   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1534   ok (result != 0,
1535       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1536   
1537   /* set text with no flag to keep undo stack should not set modify flag */
1538   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1539   setText.flags = 0;
1540   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1541   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1542   ok (result == 0,
1543       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1544   
1545   /* WM_SETTEXT doesn't modify */
1546   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1547   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1548   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1549   ok (result == 0,
1550       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1551   
1552   /* clear the text */
1553   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1554   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1555   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1556   ok (result == 0,
1557       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1558   
1559   /* replace text */
1560   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1561   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1562   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1563   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1564   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1565   ok (result != 0,
1566       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1567   
1568   /* copy/paste text 1 */
1569   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1570   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1571   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1572   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1573   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1574   ok (result != 0,
1575       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1576   
1577   /* copy/paste text 2 */
1578   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1579   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1580   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1581   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1582   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1583   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1584   ok (result != 0,
1585       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1586   
1587   /* press char */
1588   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1589   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1590   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1591   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1592   ok (result != 0,
1593       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1594
1595   /* press del */
1596   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1597   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1598   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1599   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1600   ok (result != 0,
1601       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1602   
1603   /* set char format */
1604   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1605   cf2.cbSize = sizeof(CHARFORMAT2);
1606   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1607              (LPARAM) &cf2);
1608   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1609   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1610   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1611   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1612   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1613   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1614   ok (result != 0,
1615       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1616   
1617   /* set para format */
1618   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1619   pf2.cbSize = sizeof(PARAFORMAT2);
1620   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1621              (LPARAM) &pf2);
1622   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1623   pf2.wAlignment = PFA_RIGHT;
1624   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1625   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1626   ok (result == 0,
1627       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1628
1629   /* EM_STREAM */
1630   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1631   es.dwCookie = (DWORD_PTR)&streamText;
1632   es.dwError = 0;
1633   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1634   SendMessage(hwndRichEdit, EM_STREAMIN, 
1635               (WPARAM)(SF_TEXT), (LPARAM)&es);
1636   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1637   ok (result != 0,
1638       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1639
1640   DestroyWindow(hwndRichEdit);
1641 }
1642
1643 struct exsetsel_s {
1644   long min;
1645   long max;
1646   long expected_retval;
1647   int expected_getsel_start;
1648   int expected_getsel_end;
1649   int _exsetsel_todo_wine;
1650   int _getsel_todo_wine;
1651 };
1652
1653 const struct exsetsel_s exsetsel_tests[] = {
1654   /* sanity tests */
1655   {5, 10, 10, 5, 10, 0, 0},
1656   {15, 17, 17, 15, 17, 0, 0},
1657   /* test cpMax > strlen() */
1658   {0, 100, 18, 0, 18, 0, 1},
1659   /* test cpMin == cpMax */
1660   {5, 5, 5, 5, 5, 0, 0},
1661   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1662   {-1, 0, 5, 5, 5, 0, 0},
1663   {-1, 17, 5, 5, 5, 0, 0},
1664   {-1, 18, 5, 5, 5, 0, 0},
1665   /* test cpMin < 0 && cpMax < 0 */
1666   {-1, -1, 17, 17, 17, 0, 0},
1667   {-4, -5, 17, 17, 17, 0, 0},
1668   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1669   {0, -1, 18, 0, 18, 0, 1},
1670   {17, -5, 18, 17, 18, 0, 1},
1671   {18, -3, 17, 17, 17, 0, 0},
1672   /* test if cpMin > cpMax */
1673   {15, 19, 18, 15, 18, 0, 1},
1674   {19, 15, 18, 15, 18, 0, 1}
1675 };
1676
1677 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1678     CHARRANGE cr;
1679     long result;
1680     int start, end;
1681
1682     cr.cpMin = setsel->min;
1683     cr.cpMax = setsel->max;
1684     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1685
1686     if (setsel->_exsetsel_todo_wine) {
1687         todo_wine {
1688             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1689         }
1690     } else {
1691         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1692     }
1693
1694     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1695
1696     if (setsel->_getsel_todo_wine) {
1697         todo_wine {
1698             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);
1699         }
1700     } else {
1701         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);
1702     }
1703 }
1704
1705 static void test_EM_EXSETSEL(void)
1706 {
1707     HWND hwndRichEdit = new_richedit(NULL);
1708     int i;
1709     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1710
1711     /* sending some text to the window */
1712     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1713     /*                                                 01234567890123456*/
1714     /*                                                          10      */
1715
1716     for (i = 0; i < num_tests; i++) {
1717         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1718     }
1719
1720     DestroyWindow(hwndRichEdit);
1721 }
1722
1723 static void test_EM_REPLACESEL(void)
1724 {
1725     HWND hwndRichEdit = new_richedit(NULL);
1726     char buffer[1024] = {0};
1727     int r;
1728     GETTEXTEX getText;
1729     CHARRANGE cr;
1730
1731     /* sending some text to the window */
1732     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1733     /*                                                 01234567890123456*/
1734     /*                                                          10      */
1735
1736     /* FIXME add more tests */
1737     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1738     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1739     ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
1740     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1741     r = strcmp(buffer, "testing");
1742     ok(0 == r, "expected %d, got %d\n", 0, r);
1743
1744     DestroyWindow(hwndRichEdit);
1745
1746     hwndRichEdit = new_richedit(NULL);
1747
1748     /* Test behavior with carriage returns and newlines */
1749     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1750     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
1751     ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
1752     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1753     r = strcmp(buffer, "RichEdit1");
1754     ok(0 == r, "expected %d, got %d\n", 0, r);
1755     getText.cb = 1024;
1756     getText.codepage = CP_ACP;
1757     getText.flags = GT_DEFAULT;
1758     getText.lpDefaultChar = NULL;
1759     getText.lpUsedDefaultChar = NULL;
1760     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1761     ok(strcmp(buffer, "RichEdit1") == 0,
1762       "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
1763
1764     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1765     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
1766     ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
1767     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1768     r = strcmp(buffer, "RichEdit1\r\n");
1769     ok(0 == r, "expected %d, got %d\n", 0, r);
1770     getText.cb = 1024;
1771     getText.codepage = CP_ACP;
1772     getText.flags = GT_DEFAULT;
1773     getText.lpDefaultChar = NULL;
1774     getText.lpUsedDefaultChar = NULL;
1775     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1776     ok(strcmp(buffer, "RichEdit1\r") == 0,
1777       "EM_GETTEXTEX returned incorrect string\n");
1778
1779     /* Win98's riched20 and WinXP's riched20 disagree on what to return from
1780        EM_REPLACESEL. The general rule seems to be that Win98's riched20
1781        returns the number of characters *inserted* into the control (after
1782        required conversions), but WinXP's riched20 returns the number of
1783        characters interpreted from the original lParam. Wine's builtin riched20
1784        implements the WinXP behavior.
1785      */
1786     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1787     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
1788     ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
1789         "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
1790
1791     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1792     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1793     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1794     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1795
1796     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1797     r = strcmp(buffer, "RichEdit1\r\n");
1798     ok(0 == r, "expected %d, got %d\n", 0, r);
1799     getText.cb = 1024;
1800     getText.codepage = CP_ACP;
1801     getText.flags = GT_DEFAULT;
1802     getText.lpDefaultChar = NULL;
1803     getText.lpUsedDefaultChar = NULL;
1804     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1805     ok(strcmp(buffer, "RichEdit1\r") == 0,
1806       "EM_GETTEXTEX returned incorrect string\n");
1807
1808     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1809     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1810     ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1811     ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1812
1813     /* The following tests show that richedit should handle the special \r\r\n
1814        sequence by turning it into a single space on insertion. However,
1815        EM_REPLACESEL on WinXP returns the number of characters in the original
1816        string.
1817      */
1818
1819     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1820     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
1821     ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
1822     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1823     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1824     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1825     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1826
1827     /* Test the actual string */
1828     getText.cb = 1024;
1829     getText.codepage = CP_ACP;
1830     getText.flags = GT_DEFAULT;
1831     getText.lpDefaultChar = NULL;
1832     getText.lpUsedDefaultChar = NULL;
1833     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1834     ok(strcmp(buffer, "\r\r") == 0,
1835       "EM_GETTEXTEX returned incorrect string\n");
1836
1837     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1838     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
1839     ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
1840         "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
1841     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1842     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1843     ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
1844     ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
1845
1846     /* Test the actual string */
1847     getText.cb = 1024;
1848     getText.codepage = CP_ACP;
1849     getText.flags = GT_DEFAULT;
1850     getText.lpDefaultChar = NULL;
1851     getText.lpUsedDefaultChar = NULL;
1852     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1853     ok(strcmp(buffer, " ") == 0,
1854       "EM_GETTEXTEX returned incorrect string\n");
1855
1856     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1857     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
1858     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1859         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1860     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1861     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1862     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1863     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1864
1865     /* Test the actual string */
1866     getText.cb = 1024;
1867     getText.codepage = CP_ACP;
1868     getText.flags = GT_DEFAULT;
1869     getText.lpDefaultChar = NULL;
1870     getText.lpUsedDefaultChar = NULL;
1871     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1872     ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
1873       "EM_GETTEXTEX returned incorrect string\n");
1874
1875     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1876     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
1877     ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
1878         "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
1879     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1880     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1881     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1882     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1883
1884     /* Test the actual string */
1885     getText.cb = 1024;
1886     getText.codepage = CP_ACP;
1887     getText.flags = GT_DEFAULT;
1888     getText.lpDefaultChar = NULL;
1889     getText.lpUsedDefaultChar = NULL;
1890     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1891     ok(strcmp(buffer, " \r") == 0,
1892       "EM_GETTEXTEX returned incorrect string\n");
1893
1894     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1895     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
1896     ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
1897         "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
1898     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1899     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1900     ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
1901     ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
1902
1903     /* Test the actual string */
1904     getText.cb = 1024;
1905     getText.codepage = CP_ACP;
1906     getText.flags = GT_DEFAULT;
1907     getText.lpDefaultChar = NULL;
1908     getText.lpUsedDefaultChar = NULL;
1909     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1910     ok(strcmp(buffer, " \r\r") == 0,
1911       "EM_GETTEXTEX returned incorrect string\n");
1912
1913     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1914     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
1915     ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
1916         "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
1917     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1918     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1919     ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
1920     ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
1921
1922     /* Test the actual string */
1923     getText.cb = 1024;
1924     getText.codepage = CP_ACP;
1925     getText.flags = GT_DEFAULT;
1926     getText.lpDefaultChar = NULL;
1927     getText.lpUsedDefaultChar = NULL;
1928     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1929     ok(strcmp(buffer, "\rX\r\r\r") == 0,
1930       "EM_GETTEXTEX returned incorrect string\n");
1931
1932     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1933     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
1934     ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
1935     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1936     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1937     ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1938     ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1939
1940     /* Test the actual string */
1941     getText.cb = 1024;
1942     getText.codepage = CP_ACP;
1943     getText.flags = GT_DEFAULT;
1944     getText.lpDefaultChar = NULL;
1945     getText.lpUsedDefaultChar = NULL;
1946     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1947     ok(strcmp(buffer, "\r\r") == 0,
1948       "EM_GETTEXTEX returned incorrect string\n");
1949
1950     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1951     r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
1952     ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1953         "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1954     r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1955     ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1956     ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1957     ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1958
1959     /* Test the actual string */
1960     getText.cb = 1024;
1961     getText.codepage = CP_ACP;
1962     getText.flags = GT_DEFAULT;
1963     getText.lpDefaultChar = NULL;
1964     getText.lpUsedDefaultChar = NULL;
1965     SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1966     ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
1967       "EM_GETTEXTEX returned incorrect string\n");
1968
1969     DestroyWindow(hwndRichEdit);
1970 }
1971
1972 static void test_WM_PASTE(void)
1973 {
1974     MSG msg;
1975     int result;
1976     char buffer[1024] = {0};
1977     char key_info[][3] =
1978     {
1979         /* VirtualKey, ScanCode, WM_CHAR code */
1980         {'C', 0x2e,  3},        /* Ctrl-C */
1981         {'X', 0x2d, 24},        /* Ctrl-X */
1982         {'V', 0x2f, 22},        /* Ctrl-V */
1983         {'Z', 0x2c, 26},        /* Ctrl-Z */
1984         {'Y', 0x15, 25},        /* Ctrl-Y */
1985     };
1986     const char* text1 = "testing paste\r";
1987     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
1988     const char* text1_after = "testing paste\r\n";
1989     const char* text2 = "testing paste\r\rtesting paste";
1990     const char* text2_after = "testing paste\r\n\r\ntesting paste";
1991     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1992     HWND hwndRichEdit = new_richedit(NULL);
1993
1994     /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
1995        messages, probably because it inspects the keyboard state itself.
1996        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
1997      */
1998 #define SEND_CTRL_KEY(hwnd, k) \
1999     keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2000     keybd_event(k[0], k[1], 0, 0);\
2001     keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2002     keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2003     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2004         TranslateMessage(&msg); \
2005         DispatchMessage(&msg); \
2006     }
2007
2008 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2009 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2010 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2011 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2012 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2013
2014     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2015     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2016
2017     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2018     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2019     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2020     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2021     /* Pasted text should be visible at this step */
2022     result = strcmp(text1_step1, buffer);
2023     ok(result == 0,
2024         "test paste: strcmp = %i\n", result);
2025     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2026     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2027     /* Text should be the same as before (except for \r -> \r\n conversion) */
2028     result = strcmp(text1_after, buffer);
2029     ok(result == 0,
2030         "test paste: strcmp = %i\n", result);
2031
2032     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2033     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2034     SEND_CTRL_C(hwndRichEdit)   /* Copy */
2035     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2036     SEND_CTRL_V(hwndRichEdit)   /* Paste */
2037     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2038     /* Pasted text should be visible at this step */
2039     result = strcmp(text3, buffer);
2040     ok(result == 0,
2041         "test paste: strcmp = %i\n", result);
2042     SEND_CTRL_Z(hwndRichEdit)   /* Undo */
2043     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2044     /* Text should be the same as before (except for \r -> \r\n conversion) */
2045     result = strcmp(text2_after, buffer);
2046     ok(result == 0,
2047         "test paste: strcmp = %i\n", result);
2048     SEND_CTRL_Y(hwndRichEdit)   /* Redo */
2049     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2050     /* Text should revert to post-paste state */
2051     result = strcmp(buffer,text3);
2052     ok(result == 0,
2053         "test paste: strcmp = %i\n", result);
2054
2055     DestroyWindow(hwndRichEdit);
2056 }
2057
2058 static void test_EM_FORMATRANGE(void)
2059 {
2060   int r;
2061   FORMATRANGE fr;
2062   HDC hdc;
2063   HWND hwndRichEdit = new_richedit(NULL);
2064
2065   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2066
2067   hdc = GetDC(hwndRichEdit);
2068   ok(hdc != NULL, "Could not get HDC\n");
2069
2070   fr.hdc = fr.hdcTarget = hdc;
2071   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2072   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2073   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2074   fr.chrg.cpMin = 0;
2075   fr.chrg.cpMax = 20;
2076
2077   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2078   todo_wine {
2079     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2080   }
2081
2082   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2083   todo_wine {
2084     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2085   }
2086
2087   fr.chrg.cpMin = 0;
2088   fr.chrg.cpMax = 10;
2089
2090   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2091   todo_wine {
2092     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2093   }
2094
2095   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2096   todo_wine {
2097     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2098   }
2099
2100   DestroyWindow(hwndRichEdit);
2101 }
2102
2103 static int nCallbackCount = 0;
2104
2105 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2106                                  LONG cb, LONG* pcb)
2107 {
2108   const char text[] = {'t','e','s','t'};
2109
2110   if (sizeof(text) <= cb)
2111   {
2112     if ((int)dwCookie != nCallbackCount)
2113     {
2114       *pcb = 0;
2115       return 0;
2116     }
2117
2118     memcpy (pbBuff, text, sizeof(text));
2119     *pcb = sizeof(text);
2120
2121     nCallbackCount++;
2122
2123     return 0;
2124   }
2125   else
2126     return 1; /* indicates callback failed */
2127 }
2128
2129 static void test_EM_StreamIn_Undo(void)
2130 {
2131   /* The purpose of this test is to determine when a EM_StreamIn should be
2132    * undoable. This is important because WM_PASTE currently uses StreamIn and
2133    * pasting should always be undoable but streaming isn't always.
2134    *
2135    * cases to test:
2136    * StreamIn plain text without SFF_SELECTION.
2137    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2138    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2139    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2140    * Feel free to add tests for other text modes or StreamIn things.
2141    */
2142
2143
2144   HWND hwndRichEdit = new_richedit(NULL);
2145   LRESULT result;
2146   EDITSTREAM es;
2147   char buffer[1024] = {0};
2148   const char randomtext[] = "Some text";
2149
2150   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2151
2152   /* StreamIn, no SFF_SELECTION */
2153   es.dwCookie = nCallbackCount;
2154   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2155   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2156   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2157   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2158   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2159   result = strcmp (buffer,"test");
2160   ok (result  == 0,
2161       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2162
2163   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2164   ok (result == FALSE,
2165       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2166
2167   /* StreamIn, SFF_SELECTION, but nothing selected */
2168   es.dwCookie = nCallbackCount;
2169   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2170   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2171   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2172   SendMessage(hwndRichEdit, EM_STREAMIN,
2173               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2174   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2175   result = strcmp (buffer,"testSome text");
2176   ok (result  == 0,
2177       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2178
2179   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2180   ok (result == TRUE,
2181      "EM_STREAMIN with SFF_SELECTION but no selection set "
2182       "should create an undo\n");
2183
2184   /* StreamIn, SFF_SELECTION, with a selection */
2185   es.dwCookie = nCallbackCount;
2186   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2187   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2188   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2189   SendMessage(hwndRichEdit, EM_STREAMIN,
2190               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2191   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2192   result = strcmp (buffer,"Sometesttext");
2193   ok (result  == 0,
2194       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2195
2196   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2197   ok (result == TRUE,
2198       "EM_STREAMIN with SFF_SELECTION and selection set "
2199       "should create an undo\n");
2200
2201 }
2202
2203 static BOOL is_em_settextex_supported(HWND hwnd)
2204 {
2205     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2206     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2207 }
2208
2209 static void test_unicode_conversions(void)
2210 {
2211     static const WCHAR tW[] = {'t',0};
2212     static const WCHAR teW[] = {'t','e',0};
2213     static const WCHAR textW[] = {'t','e','s','t',0};
2214     static const char textA[] = "test";
2215     char bufA[64];
2216     WCHAR bufW[64];
2217     HWND hwnd;
2218     int is_win9x, em_settextex_supported, ret;
2219
2220     is_win9x = GetVersion() & 0x80000000;
2221
2222 #define set_textA(hwnd, wm_set_text, txt) \
2223     do { \
2224         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2225         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2226         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2227         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2228         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2229     } while(0)
2230 #define expect_textA(hwnd, wm_get_text, txt) \
2231     do { \
2232         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2233         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2234         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2235         memset(bufA, 0xAA, sizeof(bufA)); \
2236         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2237         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2238         ret = lstrcmpA(bufA, txt); \
2239         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2240     } while(0)
2241
2242 #define set_textW(hwnd, wm_set_text, txt) \
2243     do { \
2244         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2245         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2246         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2247         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2248         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2249     } while(0)
2250 #define expect_textW(hwnd, wm_get_text, txt) \
2251     do { \
2252         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2253         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2254         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2255         memset(bufW, 0xAA, sizeof(bufW)); \
2256         if (is_win9x) \
2257         { \
2258             assert(wm_get_text == EM_GETTEXTEX); \
2259             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2260             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2261         } \
2262         else \
2263         { \
2264             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2265             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2266         } \
2267         ret = lstrcmpW(bufW, txt); \
2268         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2269     } while(0)
2270 #define expect_empty(hwnd, wm_get_text) \
2271     do { \
2272         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2273         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2274         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2275         memset(bufA, 0xAA, sizeof(bufA)); \
2276         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2277         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2278         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2279     } while(0)
2280
2281     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2282                            0, 0, 200, 60, 0, 0, 0, 0);
2283     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2284
2285     ret = IsWindowUnicode(hwnd);
2286     if (is_win9x)
2287         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2288     else
2289         ok(ret, "RichEdit20W should be unicode under NT\n");
2290
2291     /* EM_SETTEXTEX is supported starting from version 3.0 */
2292     em_settextex_supported = is_em_settextex_supported(hwnd);
2293     trace("EM_SETTEXTEX is %ssupported on this platform\n",
2294           em_settextex_supported ? "" : "NOT ");
2295
2296     expect_empty(hwnd, WM_GETTEXT);
2297     expect_empty(hwnd, EM_GETTEXTEX);
2298
2299     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2300     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2301     expect_textA(hwnd, WM_GETTEXT, "t");
2302     expect_textA(hwnd, EM_GETTEXTEX, "t");
2303     expect_textW(hwnd, EM_GETTEXTEX, tW);
2304
2305     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2306     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2307     expect_textA(hwnd, WM_GETTEXT, "te");
2308     expect_textA(hwnd, EM_GETTEXTEX, "te");
2309     expect_textW(hwnd, EM_GETTEXTEX, teW);
2310
2311     set_textA(hwnd, WM_SETTEXT, NULL);
2312     expect_empty(hwnd, WM_GETTEXT);
2313     expect_empty(hwnd, EM_GETTEXTEX);
2314
2315     if (is_win9x)
2316         set_textA(hwnd, WM_SETTEXT, textW);
2317     else
2318         set_textA(hwnd, WM_SETTEXT, textA);
2319     expect_textA(hwnd, WM_GETTEXT, textA);
2320     expect_textA(hwnd, EM_GETTEXTEX, textA);
2321     expect_textW(hwnd, EM_GETTEXTEX, textW);
2322
2323     if (em_settextex_supported)
2324     {
2325         set_textA(hwnd, EM_SETTEXTEX, textA);
2326         expect_textA(hwnd, WM_GETTEXT, textA);
2327         expect_textA(hwnd, EM_GETTEXTEX, textA);
2328         expect_textW(hwnd, EM_GETTEXTEX, textW);
2329     }
2330
2331     if (!is_win9x)
2332     {
2333         set_textW(hwnd, WM_SETTEXT, textW);
2334         expect_textW(hwnd, WM_GETTEXT, textW);
2335         expect_textA(hwnd, WM_GETTEXT, textA);
2336         expect_textW(hwnd, EM_GETTEXTEX, textW);
2337         expect_textA(hwnd, EM_GETTEXTEX, textA);
2338
2339         if (em_settextex_supported)
2340         {
2341             set_textW(hwnd, EM_SETTEXTEX, textW);
2342             expect_textW(hwnd, WM_GETTEXT, textW);
2343             expect_textA(hwnd, WM_GETTEXT, textA);
2344             expect_textW(hwnd, EM_GETTEXTEX, textW);
2345             expect_textA(hwnd, EM_GETTEXTEX, textA);
2346         }
2347     }
2348     DestroyWindow(hwnd);
2349
2350     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2351                            0, 0, 200, 60, 0, 0, 0, 0);
2352     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2353
2354     ret = IsWindowUnicode(hwnd);
2355     ok(!ret, "RichEdit20A should NOT be unicode\n");
2356
2357     set_textA(hwnd, WM_SETTEXT, textA);
2358     expect_textA(hwnd, WM_GETTEXT, textA);
2359     expect_textA(hwnd, EM_GETTEXTEX, textA);
2360     expect_textW(hwnd, EM_GETTEXTEX, textW);
2361
2362     if (em_settextex_supported)
2363     {
2364         set_textA(hwnd, EM_SETTEXTEX, textA);
2365         expect_textA(hwnd, WM_GETTEXT, textA);
2366         expect_textA(hwnd, EM_GETTEXTEX, textA);
2367         expect_textW(hwnd, EM_GETTEXTEX, textW);
2368     }
2369
2370     if (!is_win9x)
2371     {
2372         set_textW(hwnd, WM_SETTEXT, textW);
2373         expect_textW(hwnd, WM_GETTEXT, textW);
2374         expect_textA(hwnd, WM_GETTEXT, textA);
2375         expect_textW(hwnd, EM_GETTEXTEX, textW);
2376         expect_textA(hwnd, EM_GETTEXTEX, textA);
2377
2378         if (em_settextex_supported)
2379         {
2380             set_textW(hwnd, EM_SETTEXTEX, textW);
2381             expect_textW(hwnd, WM_GETTEXT, textW);
2382             expect_textA(hwnd, WM_GETTEXT, textA);
2383             expect_textW(hwnd, EM_GETTEXTEX, textW);
2384             expect_textA(hwnd, EM_GETTEXTEX, textA);
2385         }
2386     }
2387     DestroyWindow(hwnd);
2388 }
2389
2390 static void test_WM_CHAR(void)
2391 {
2392     HWND hwnd;
2393     int ret;
2394     const char * char_list = "abc\rabc\r";
2395     const char * expected_content_single = "abcabc";
2396     const char * expected_content_multi = "abc\r\nabc\r\n";
2397     char buffer[64] = {0};
2398     const char * p;
2399
2400     /* single-line control must IGNORE carriage returns */
2401     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2402                            0, 0, 200, 60, 0, 0, 0, 0);
2403     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2404
2405     p = char_list;
2406     while (*p != '\0') {
2407         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2408         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2409         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2410         SendMessageA(hwnd, WM_KEYUP, *p, 1);
2411         p++;
2412     }
2413
2414     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2415     ret = strcmp(buffer, expected_content_single);
2416     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2417
2418     DestroyWindow(hwnd);
2419
2420     /* multi-line control inserts CR normally */
2421     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2422                            0, 0, 200, 60, 0, 0, 0, 0);
2423     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2424
2425     p = char_list;
2426     while (*p != '\0') {
2427         SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2428         ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2429         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2430         SendMessageA(hwnd, WM_KEYUP, *p, 1);
2431         p++;
2432     }
2433
2434     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2435     ret = strcmp(buffer, expected_content_multi);
2436     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2437
2438     DestroyWindow(hwnd);
2439 }
2440
2441 static void test_EM_GETTEXTLENGTHEX(void)
2442 {
2443     HWND hwnd;
2444     GETTEXTLENGTHEX gtl;
2445     int ret;
2446     const char * test_string = "a\nb\n\n\r\n";
2447     const char * test_string_after = "a";
2448     char buffer[64] = {0};
2449
2450     /* single line */
2451     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2452                            0, 0, 200, 60, 0, 0, 0, 0);
2453     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2454
2455     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2456     gtl.codepage = CP_ACP;
2457     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2458     ok(ret == 0, "ret %d\n",ret);
2459
2460     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2461     gtl.codepage = CP_ACP;
2462     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2463     ok(ret == 0, "ret %d\n",ret);
2464
2465     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2466
2467     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2468     gtl.codepage = CP_ACP;
2469     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2470     ok(ret == 1, "ret %d\n",ret);
2471
2472     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2473     gtl.codepage = CP_ACP;
2474     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2475     ok(ret == 1, "ret %d\n",ret);
2476
2477     SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2478     ret = strcmp(buffer, test_string_after);
2479     ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2480
2481     DestroyWindow(hwnd);
2482
2483     /* multi line */
2484     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2485                            0, 0, 200, 60, 0, 0, 0, 0);
2486     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2487
2488     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2489     gtl.codepage = CP_ACP;
2490     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2491     todo_wine ok(ret == 0, "ret %d\n",ret);
2492
2493     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2494     gtl.codepage = CP_ACP;
2495     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2496     ok(ret == 0, "ret %d\n",ret);
2497
2498     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2499
2500     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2501     gtl.codepage = CP_ACP;
2502     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2503     todo_wine ok(ret == 10, "ret %d\n",ret);
2504
2505     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2506     gtl.codepage = CP_ACP;
2507     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2508     ok(ret == 6, "ret %d\n",ret);
2509
2510     DestroyWindow(hwnd);
2511 }
2512
2513
2514 /* globals that parent and child access when checking event masks & notifications */
2515 static HWND eventMaskEditHwnd = 0;
2516 static int queriedEventMask;
2517 static int watchForEventMask = 0;
2518
2519 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2520 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2521 {
2522     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2523     {
2524       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2525     }
2526     return DefWindowProcA(hwnd, message, wParam, lParam);
2527 }
2528
2529 /* test event masks in combination with WM_COMMAND */
2530 static void test_eventMask(void)
2531 {
2532     HWND parent;
2533     int ret;
2534     WNDCLASSA cls;
2535     const char text[] = "foo bar\n";
2536     int eventMask;
2537
2538     /* register class to capture WM_COMMAND */
2539     cls.style = 0;
2540     cls.lpfnWndProc = ParentMsgCheckProcA;
2541     cls.cbClsExtra = 0;
2542     cls.cbWndExtra = 0;
2543     cls.hInstance = GetModuleHandleA(0);
2544     cls.hIcon = 0;
2545     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2546     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2547     cls.lpszMenuName = NULL;
2548     cls.lpszClassName = "EventMaskParentClass";
2549     if(!RegisterClassA(&cls)) assert(0);
2550
2551     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2552                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
2553     ok (parent != 0, "Failed to create parent window\n");
2554
2555     eventMaskEditHwnd = new_richedit(parent);
2556     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2557
2558     eventMask = ENM_CHANGE | ENM_UPDATE;
2559     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2560     ok(ret == ENM_NONE, "wrong event mask\n");
2561     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2562     ok(ret == eventMask, "failed to set event mask\n");
2563
2564     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2565     queriedEventMask = 0;  /* initialize to something other than we expect */
2566     watchForEventMask = EN_CHANGE;
2567     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2568     ok(ret == TRUE, "failed to set text\n");
2569     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2570        notification in response to WM_SETTEXT */
2571     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2572             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2573
2574 }
2575
2576
2577 START_TEST( editor )
2578 {
2579   MSG msg;
2580   time_t end;
2581
2582   /* Must explicitly LoadLibrary(). The test has no references to functions in
2583    * RICHED20.DLL, so the linker doesn't actually link to it. */
2584   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2585   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2586
2587   test_WM_CHAR();
2588   test_EM_FINDTEXT();
2589   test_EM_GETLINE();
2590   test_EM_SCROLLCARET();
2591   test_EM_SCROLL();
2592   test_WM_SETTEXT();
2593   test_EM_SETCHARFORMAT();
2594   test_EM_SETTEXTMODE();
2595   test_TM_PLAINTEXT();
2596   test_EM_SETOPTIONS();
2597   test_WM_GETTEXT();
2598   test_EM_AUTOURLDETECT();
2599   test_EM_SETUNDOLIMIT();
2600   test_ES_PASSWORD();
2601   test_EM_SETTEXTEX();
2602   test_EM_LIMITTEXT();
2603   test_EM_EXLIMITTEXT();
2604   test_EM_GETLIMITTEXT();
2605   test_WM_SETFONT();
2606   test_EM_GETMODIFY();
2607   test_EM_EXSETSEL();
2608   test_WM_PASTE();
2609   test_EM_StreamIn_Undo();
2610   test_EM_FORMATRANGE();
2611   test_unicode_conversions();
2612   test_EM_GETTEXTLENGTHEX();
2613   test_EM_REPLACESEL();
2614   test_eventMask();
2615
2616   /* Set the environment variable WINETEST_RICHED20 to keep windows
2617    * responsive and open for 30 seconds. This is useful for debugging.
2618    *
2619    * The message pump uses PeekMessage() to empty the queue and then sleeps for
2620    * 50ms before retrying the queue. */
2621   end = time(NULL) + 30;
2622   if (getenv( "WINETEST_RICHED20" )) {
2623     while (time(NULL) < end) {
2624       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2625         TranslateMessage(&msg);
2626         DispatchMessage(&msg);
2627       } else {
2628         Sleep(50);
2629       }
2630     }
2631   }
2632
2633   OleFlushClipboard();
2634   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
2635 }