riched20: Fix WM_GETTEXT to change \r to \r\n.
[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_SETTEXTMODE(void)
337 {
338   HWND hwndRichEdit = new_richedit(NULL);
339   CHARFORMAT2 cf2, cf2test;
340   CHARRANGE cr;
341   int rc = 0;
342
343   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
344   /*Insert text into the control*/
345
346   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
347
348   /*Attempt to change the control to plain text mode*/
349   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
350   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
351
352   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
353   If rich text is pasted, it should have the same formatting as the rest
354   of the text in the control*/
355
356   /*Italicize the text
357   *NOTE: If the default text was already italicized, the test will simply
358   reverse; in other words, it will copy a regular "wine" into a plain
359   text window that uses an italicized format*/
360   cf2.cbSize = sizeof(CHARFORMAT2);
361   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
362              (LPARAM) &cf2);
363
364   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
365   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
366
367   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
368   however, SCF_ALL has been implemented*/
369   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
370   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
371
372   /*Select the string "wine"*/
373   cr.cpMin = 0;
374   cr.cpMax = 4;
375   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
376
377   /*Copy the italicized "wine" to the clipboard*/
378   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
379
380   /*Reset the formatting to default*/
381   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
382   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
383
384   /*Clear the text in the control*/
385   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
386
387   /*Switch to Plain Text Mode*/
388   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
389   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
390
391   /*Input "wine" again in normal format*/
392   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
393
394   /*Paste the italicized "wine" into the control*/
395   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
396
397   /*Select a character from the first "wine" string*/
398   cr.cpMin = 2;
399   cr.cpMax = 3;
400   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401
402   /*Retrieve its formatting*/
403   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
404               (LPARAM) &cf2);
405
406   /*Select a character from the second "wine" string*/
407   cr.cpMin = 5;
408   cr.cpMax = 6;
409   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
410
411   /*Retrieve its formatting*/
412   cf2test.cbSize = sizeof(CHARFORMAT2);
413   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
414                (LPARAM) &cf2test);
415
416   /*Compare the two formattings*/
417     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
418       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
419        cf2.dwEffects, cf2test.dwEffects);
420   /*Test TM_RICHTEXT by: switching back to Rich Text mode
421                          printing "wine" in the current format(normal)
422                          pasting "wine" from the clipboard(italicized)
423                          comparing the two formats(should differ)*/
424
425   /*Attempt to switch with text in control*/
426   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
427   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
428
429   /*Clear control*/
430   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
431
432   /*Switch into Rich Text mode*/
433   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
434   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
435
436   /*Print "wine" in normal formatting into the control*/
437   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
438
439   /*Paste italicized "wine" into the control*/
440   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
441
442   /*Select text from the first "wine" string*/
443   cr.cpMin = 1;
444   cr.cpMax = 3;
445   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446
447   /*Retrieve its formatting*/
448   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
449                 (LPARAM) &cf2);
450
451   /*Select text from the second "wine" string*/
452   cr.cpMin = 6;
453   cr.cpMax = 7;
454   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
455
456   /*Retrieve its formatting*/
457   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
458                 (LPARAM) &cf2test);
459
460   /*Test that the two formattings are not the same*/
461   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
462       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
463       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
464
465   DestroyWindow(hwndRichEdit);
466 }
467
468 static void test_TM_PLAINTEXT(void)
469 {
470   /*Tests plain text properties*/
471
472   HWND hwndRichEdit = new_richedit(NULL);
473   CHARFORMAT2 cf2, cf2test;
474   CHARRANGE cr;
475
476   /*Switch to plain text mode*/
477
478   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
479   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
480
481   /*Fill control with text*/
482
483   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
484
485   /*Select some text and bold it*/
486
487   cr.cpMin = 10;
488   cr.cpMax = 20;
489   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
490   cf2.cbSize = sizeof(CHARFORMAT2);
491   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
492               (LPARAM) &cf2);
493
494   cf2.dwMask = CFM_BOLD | cf2.dwMask;
495   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
496
497   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
498
499   /*Get the formatting of those characters*/
500
501   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
502
503   /*Get the formatting of some other characters*/
504   cf2test.cbSize = sizeof(CHARFORMAT2);
505   cr.cpMin = 21;
506   cr.cpMax = 30;
507   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
508   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
509
510   /*Test that they are the same as plain text allows only one formatting*/
511
512   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
513      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
514      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
515   
516   /*Fill the control with a "wine" string, which when inserted will be bold*/
517
518   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
519
520   /*Copy the bolded "wine" string*/
521
522   cr.cpMin = 0;
523   cr.cpMax = 4;
524   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
525   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
526
527   /*Swap back to rich text*/
528
529   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
530   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
531
532   /*Set the default formatting to bold italics*/
533
534   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
535   cf2.dwMask |= CFM_ITALIC;
536   cf2.dwEffects ^= CFE_ITALIC;
537   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
538
539   /*Set the text in the control to "wine", which will be bold and italicized*/
540
541   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
542
543   /*Paste the plain text "wine" string, which should take the insert
544    formatting, which at the moment is bold italics*/
545
546   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
547
548   /*Select the first "wine" string and retrieve its formatting*/
549
550   cr.cpMin = 1;
551   cr.cpMax = 3;
552   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
553   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
554
555   /*Select the second "wine" string and retrieve its formatting*/
556
557   cr.cpMin = 5;
558   cr.cpMax = 7;
559   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
560   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
561
562   /*Compare the two formattings. They should be the same.*/
563
564   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
565      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
566      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
567   DestroyWindow(hwndRichEdit);
568 }
569
570 static void test_WM_GETTEXT(void)
571 {
572     HWND hwndRichEdit = new_richedit(NULL);
573     static const char text[] = "Hello. My name is RichEdit!";
574     char buffer[1024] = {0};
575     int result;
576
577     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
578     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
579     result = strcmp(buffer,text);
580     ok(result == 0, 
581         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
582     DestroyWindow(hwndRichEdit);
583 }
584
585 /* FIXME: need to test unimplemented options and robustly test wparam */
586 static void test_EM_SETOPTIONS(void)
587 {
588     HWND hwndRichEdit = new_richedit(NULL);
589     static const char text[] = "Hello. My name is RichEdit!";
590     char buffer[1024] = {0};
591
592     /* NEGATIVE TESTING - NO OPTIONS SET */
593     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
594     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
595
596     /* testing no readonly by sending 'a' to the control*/
597     SetFocus(hwndRichEdit);
598     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
599     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600     ok(buffer[0]=='a', 
601        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
602     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
603
604     /* READONLY - sending 'a' to the control */
605     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
606     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
607     SetFocus(hwndRichEdit);
608     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
609     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
610     ok(buffer[0]==text[0], 
611        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
612
613     DestroyWindow(hwndRichEdit);
614 }
615
616 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
617 {
618   CHARFORMAT2W text_format;
619   int link_present = 0;
620   text_format.cbSize = sizeof(text_format);
621   SendMessage(hwnd, EM_SETSEL, 0, 0);
622   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
623   link_present = text_format.dwEffects & CFE_LINK;
624   if (is_url) 
625   { /* control text is url; should get CFE_LINK */
626         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
627   }
628   else 
629   {
630     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
631   }
632 }
633
634 static HWND new_static_wnd(HWND parent) {
635   return new_window("Static", 0, parent);
636 }
637
638 static void test_EM_AUTOURLDETECT(void)
639 {
640   struct urls_s {
641     const char *text;
642     int is_url;
643   } urls[12] = {
644     {"winehq.org", 0},
645     {"http://www.winehq.org", 1},
646     {"http//winehq.org", 0},
647     {"ww.winehq.org", 0},
648     {"www.winehq.org", 1},
649     {"ftp://192.168.1.1", 1},
650     {"ftp//192.168.1.1", 0},
651     {"mailto:your@email.com", 1},    
652     {"prospero:prosperoserver", 1},
653     {"telnet:test", 1},
654     {"news:newserver", 1},
655     {"wais:waisserver", 1}  
656   };
657
658   int i;
659   int urlRet=-1;
660   HWND hwndRichEdit, parent;
661
662   parent = new_static_wnd(NULL);
663   hwndRichEdit = new_richedit(parent);
664   /* Try and pass EM_AUTOURLDETECT some test wParam values */
665   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
666   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
667   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
668   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
669   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
670   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
671   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
672   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
673   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
674   /* for each url, check the text to see if CFE_LINK effect is present */
675   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
676     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
677     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
678     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
679     check_CFE_LINK_rcvd(hwndRichEdit, 0);
680     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
681     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
682     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
683     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
684   }
685   DestroyWindow(hwndRichEdit);
686   DestroyWindow(parent);
687 }
688
689 static void test_EM_SCROLL(void)
690 {
691   int i, j;
692   int r; /* return value */
693   int expr; /* expected return value */
694   HWND hwndRichEdit = new_richedit(NULL);
695   int y_before, y_after; /* units of lines of text */
696
697   /* test a richedit box containing a single line of text */
698   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
699   expr = 0x00010000;
700   for (i = 0; i < 4; i++) {
701     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
702
703     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
704     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
705     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
706        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
707     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
708        "(i == %d)\n", y_after, i);
709   }
710
711   /*
712    * test a richedit box that will scroll. There are two general
713    * cases: the case without any long lines and the case with a long
714    * line.
715    */
716   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
717     if (i == 0)
718       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
719     else
720       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
721                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
723                   "LONG LINE \nb\nc\nd\ne");
724     for (j = 0; j < 12; j++) /* reset scrol position to top */
725       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
726
727     /* get first visible line */
728     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
729     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
730
731     /* get new current first visible line */
732     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
733
734     ok(((r & 0xffffff00) == 0x00010000) &&
735        ((r & 0x000000ff) != 0x00000000),
736        "EM_SCROLL page down didn't scroll by a small positive number of "
737        "lines (r == 0x%08x)\n", r);
738     ok(y_after > y_before, "EM_SCROLL page down not functioning "
739        "(line %d scrolled to line %d\n", y_before, y_after);
740
741     y_before = y_after;
742     
743     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
744     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
745     ok(((r & 0xffffff00) == 0x0001ff00),
746        "EM_SCROLL page up didn't scroll by a small negative number of lines "
747        "(r == 0x%08x)\n", r);
748     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
749        "%d scrolled to line %d\n", y_before, y_after);
750     
751     y_before = y_after;
752
753     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
754
755     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
756
757     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
758        "(r == 0x%08x)\n", r);
759     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
760        "1 line (%d scrolled to %d)\n", y_before, y_after);
761
762     y_before = y_after;
763
764     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
765
766     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
767
768     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
769        "(r == 0x%08x)\n", r);
770     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
771        "line (%d scrolled to %d)\n", y_before, y_after);
772
773     y_before = y_after;
774
775     r = SendMessage(hwndRichEdit, EM_SCROLL,
776                     SB_LINEUP, 0); /* lineup beyond top */
777
778     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
779
780     ok(r == 0x00010000,
781        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
782     ok(y_before == y_after,
783        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
784
785     y_before = y_after;
786
787     r = SendMessage(hwndRichEdit, EM_SCROLL,
788                     SB_PAGEUP, 0);/*page up beyond top */
789
790     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
791
792     ok(r == 0x00010000,
793        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
794     ok(y_before == y_after,
795        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
796
797     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
798       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
799     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
800     r = SendMessage(hwndRichEdit, EM_SCROLL,
801                     SB_PAGEDOWN, 0); /* page down beyond bot */
802     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
803
804     ok(r == 0x00010000,
805        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
806     ok(y_before == y_after,
807        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
808        y_before, y_after);
809
810     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
811     SendMessage(hwndRichEdit, EM_SCROLL,
812                 SB_LINEDOWN, 0); /* line down beyond bot */
813     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
814     
815     ok(r == 0x00010000,
816        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
817     ok(y_before == y_after,
818        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
819        y_before, y_after);
820   }
821   DestroyWindow(hwndRichEdit);
822 }
823
824 static void test_EM_SETUNDOLIMIT(void)
825 {
826   /* cases we test for:
827    * default behaviour - limiting at 100 undo's 
828    * undo disabled - setting a limit of 0
829    * undo limited -  undo limit set to some to some number, like 2
830    * bad input - sending a negative number should default to 100 undo's */
831  
832   HWND hwndRichEdit = new_richedit(NULL);
833   CHARRANGE cr;
834   int i;
835   int result;
836   
837   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
838   cr.cpMin = 0;
839   cr.cpMax = 1;
840   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
841     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
842       also, multiple pastes don't combine like WM_CHAR would */
843   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
844
845   /* first case - check the default */
846   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
847   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
848     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
849   for (i=0; i<100; i++) /* Undo 100 of them */
850     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
851   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
852      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
853
854   /* second case - cannot undo */
855   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
856   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
857   SendMessage(hwndRichEdit,
858               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
859   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
860      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
861
862   /* third case - set it to an arbitrary number */
863   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
864   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
865   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
867   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
868   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
869   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
870      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
871   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
872   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
873      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
874   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
875   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
876      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
877   
878   /* fourth case - setting negative numbers should default to 100 undos */
879   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
880   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
881   ok (result == 100, 
882       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
883       
884   DestroyWindow(hwndRichEdit);
885 }
886
887 static void test_ES_PASSWORD(void)
888 {
889   /* This isn't hugely testable, so we're just going to run it through its paces */
890
891   HWND hwndRichEdit = new_richedit(NULL);
892   WCHAR result;
893
894   /* First, check the default of a regular control */
895   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
896   ok (result == 0,
897         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
898
899   /* Now, set it to something normal */
900   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
901   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
902   ok (result == 120,
903         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
904
905   /* Now, set it to something odd */
906   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
907   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
908   ok (result == 1234,
909         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
910   DestroyWindow(hwndRichEdit);
911 }
912
913 static void test_WM_SETTEXT()
914 {
915   HWND hwndRichEdit = new_richedit(NULL);
916   const char * TestItem1 = "TestSomeText";
917   const char * TestItem2 = "TestSomeText\r";
918   const char * TestItem2_after = "TestSomeText\r\n";
919   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
920   const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
921   char buf[1024] = {0};
922   LRESULT result;
923
924   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
925      any solitary \r to be converted to \r\n on return. Properly paired
926      \r\n are not affected.
927    */
928
929   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
930   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
931   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
932   ok (result == strlen(buf),
933         "WM_GETTEXT returned %ld instead of expected %u\n",
934         result, strlen(buf));
935   result = strcmp(TestItem1, buf);
936   ok(result == 0,
937         "WM_SETTEXT round trip: strcmp = %ld\n", result);
938
939   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
940   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
941   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
942   ok (result == strlen(buf),
943         "WM_GETTEXT returned %ld instead of expected %u\n",
944         result, strlen(buf));
945   result = strcmp(TestItem2_after, buf);
946   ok(result == 0,
947         "WM_SETTEXT round trip: strcmp = %ld\n", result);
948
949   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
950   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
951   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
952   ok (result == strlen(buf),
953         "WM_GETTEXT returned %ld instead of expected %u\n",
954         result, strlen(buf));
955   result = strcmp(TestItem3_after, buf);
956   ok(result == 0,
957         "WM_SETTEXT round trip: strcmp = %ld\n", result);
958
959   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3_after);
960   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
961   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
962   ok (result == strlen(buf),
963         "WM_GETTEXT returned %ld instead of expected %u\n",
964         result, strlen(buf));
965   result = strcmp(TestItem3_after, buf);
966   ok(result == 0,
967         "WM_SETTEXT round trip: strcmp = %ld\n", result);
968
969   DestroyWindow(hwndRichEdit);
970 }
971
972 static void test_EM_SETTEXTEX(void)
973 {
974   HWND hwndRichEdit = new_richedit(NULL);
975   SETTEXTEX setText;
976   GETTEXTEX getText;
977   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
978                        'S', 'o', 'm', 'e', 
979                        'T', 'e', 'x', 't', 0}; 
980   WCHAR TestItem2[] = {'T', 'e', 's', 't',
981                        'S', 'o', 'm', 'e',
982                        'T', 'e', 'x', 't',
983                        '\r', 0};
984   const char * TestItem2_after = "TestSomeText\r\n";
985 #define MAX_BUF_LEN 1024
986   WCHAR buf[MAX_BUF_LEN];
987   int result;
988   CHARRANGE cr;
989
990   setText.codepage = 1200;  /* no constant for unicode */
991   getText.codepage = 1200;  /* no constant for unicode */
992   getText.cb = MAX_BUF_LEN;
993   getText.flags = GT_DEFAULT;
994
995   setText.flags = 0;
996   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
997   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
998   ok(lstrcmpW(buf, TestItem1) == 0,
999       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1000
1001   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1002      convert \r to \r\n on return
1003    */
1004   setText.codepage = 1200;  /* no constant for unicode */
1005   getText.codepage = 1200;  /* no constant for unicode */
1006   getText.cb = MAX_BUF_LEN;
1007   getText.flags = GT_DEFAULT;
1008
1009   setText.flags = 0;
1010   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1011   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1012   ok(lstrcmpW(buf, TestItem2) == 0,
1013       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1014
1015   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1016   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1017   ok(strcmp((const char *)buf, TestItem2_after) == 0,
1018       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1019
1020   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1021                        (WPARAM)&setText, (LPARAM) NULL);
1022   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1023   
1024   ok (result == 1, 
1025       "EM_SETTEXTEX returned %d, instead of 1\n",result);
1026   ok(lstrlenW(buf) == 0,
1027       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1028   
1029   /* put some text back */
1030   setText.flags = 0;
1031   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1032   /* select some text */
1033   cr.cpMax = 1;
1034   cr.cpMin = 3;
1035   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1036   /* replace current selection */
1037   setText.flags = ST_SELECTION;
1038   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1039                        (WPARAM)&setText, (LPARAM) NULL);
1040   ok(result == 0,
1041       "EM_SETTEXTEX with NULL lParam to replace selection"
1042       " with no text should return 0. Got %i\n",
1043       result);
1044   
1045   /* put some text back */
1046   setText.flags = 0;
1047   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1048   /* select some text */
1049   cr.cpMax = 1;
1050   cr.cpMin = 3;
1051   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1052   /* replace current selection */
1053   setText.flags = ST_SELECTION;
1054   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1055                        (WPARAM)&setText, (LPARAM) TestItem1);
1056   /* get text */
1057   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1058   ok(result == lstrlenW(TestItem1),
1059       "EM_SETTEXTEX with NULL lParam to replace selection"
1060       " with no text should return 0. Got %i\n",
1061       result);
1062   ok(lstrlenW(buf) == 22,
1063       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1064       lstrlenW(buf) );
1065
1066   DestroyWindow(hwndRichEdit);
1067 }
1068
1069 static void test_EM_LIMITTEXT(void)
1070 {
1071   int ret;
1072
1073   HWND hwndRichEdit = new_richedit(NULL);
1074
1075   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1076    * about setting the length to -1 for multiline edit controls doesn't happen.
1077    */
1078
1079   /* Don't check default gettextlimit case. That's done in other tests */
1080
1081   /* Set textlimit to 100 */
1082   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1083   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1084   ok (ret == 100,
1085       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1086
1087   /* Set textlimit to 0 */
1088   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1089   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1090   ok (ret == 65536,
1091       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1092
1093   /* Set textlimit to -1 */
1094   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1095   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1096   ok (ret == -1,
1097       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1098
1099   /* Set textlimit to -2 */
1100   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1101   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1102   ok (ret == -2,
1103       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1104
1105   DestroyWindow (hwndRichEdit);
1106 }
1107
1108
1109 static void test_EM_EXLIMITTEXT(void)
1110 {
1111   int i, selBegin, selEnd, len1, len2;
1112   int result;
1113   char text[1024 + 1];
1114   char buffer[1024 + 1];
1115   int textlimit = 0; /* multiple of 100 */
1116   HWND hwndRichEdit = new_richedit(NULL);
1117   
1118   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1119   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1120   
1121   textlimit = 256000;
1122   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1123   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1124   /* set higher */
1125   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1126   
1127   textlimit = 1000;
1128   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1129   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1130   /* set lower */
1131   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1132  
1133   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1134   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1135   /* default for WParam = 0 */
1136   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1137  
1138   textlimit = sizeof(text)-1;
1139   memset(text, 'W', textlimit);
1140   text[sizeof(text)-1] = 0;
1141   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1142   /* maxed out text */
1143   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1144   
1145   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1146   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1147   len1 = selEnd - selBegin;
1148   
1149   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1150   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1151   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1152   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1153   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1154   len2 = selEnd - selBegin;
1155   
1156   ok(len1 != len2,
1157     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1158     len1,len2,i);
1159   
1160   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1161   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1162   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1163   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1164   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1165   len1 = selEnd - selBegin;
1166   
1167   ok(len1 != len2,
1168     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1169     len1,len2,i);
1170   
1171   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1172   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1173   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1174   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1175   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1176   len2 = selEnd - selBegin;
1177   
1178   ok(len1 == len2, 
1179     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1180     len1,len2,i);
1181
1182   /* set text up to the limit, select all the text, then add a char */
1183   textlimit = 5;
1184   memset(text, 'W', textlimit);
1185   text[textlimit] = 0;
1186   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1187   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1188   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1189   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1190   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1191   result = strcmp(buffer, "A");
1192   ok(0 == result, "got string = \"%s\"\n", buffer);
1193
1194   /* WM_SETTEXT not limited */
1195   textlimit = 10;
1196   memset(text, 'W', textlimit);
1197   text[textlimit] = 0;
1198   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1199   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1200   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1201   i = strlen(buffer);
1202   ok(10 == i, "expected 10 chars\n");
1203   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1204   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1205
1206   /* try inserting more text at end */
1207   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1208   ok(0 == i, "WM_CHAR wasn't processed");
1209   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1210   i = strlen(buffer);
1211   ok(10 == i, "expected 10 chars, got %i\n", i);
1212   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1213   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1214
1215   /* try inserting text at beginning */
1216   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1217   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1218   ok(0 == i, "WM_CHAR wasn't processed");
1219   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1220   i = strlen(buffer);
1221   ok(10 == i, "expected 10 chars, got %i\n", i);
1222   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1223   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1224
1225   /* WM_CHAR is limited */
1226   textlimit = 1;
1227   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1228   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1229   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1230   ok(0 == i, "WM_CHAR wasn't processed");
1231   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1232   ok(0 == i, "WM_CHAR wasn't processed");
1233   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1234   i = strlen(buffer);
1235   ok(1 == i, "expected 1 chars, got %i instead\n", i);
1236
1237   DestroyWindow(hwndRichEdit);
1238 }
1239
1240 static void test_EM_GETLIMITTEXT(void)
1241 {
1242   int i;
1243   HWND hwndRichEdit = new_richedit(NULL);
1244
1245   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1246   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1247
1248   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1249   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1250   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1251
1252   DestroyWindow(hwndRichEdit);
1253 }
1254
1255 static void test_WM_SETFONT(void)
1256 {
1257   /* There is no invalid input or error conditions for this function.
1258    * NULL wParam and lParam just fall back to their default values 
1259    * It should be noted that even if you use a gibberish name for your fonts
1260    * here, it will still work because the name is stored. They will display as
1261    * System, but will report their name to be whatever they were created as */
1262   
1263   HWND hwndRichEdit = new_richedit(NULL);
1264   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1265     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1266     FF_DONTCARE, "Marlett");
1267   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1268     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1269     FF_DONTCARE, "MS Sans Serif");
1270   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1271     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1272     FF_DONTCARE, "Courier");
1273   LOGFONTA sentLogFont;
1274   CHARFORMAT2A returnedCF2A;
1275   
1276   returnedCF2A.cbSize = sizeof(returnedCF2A);
1277   
1278   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1279   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1280   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1281
1282   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1283   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1284     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1285     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1286
1287   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1288   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1289   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1290   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1291     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1292     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1293     
1294   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1295   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1296   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1297   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1298     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1299     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1300    
1301   /* This last test is special since we send in NULL. We clear the variables
1302    * and just compare to "System" instead of the sent in font name. */
1303   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1304   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1305   returnedCF2A.cbSize = sizeof(returnedCF2A);
1306   
1307   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1308   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1309   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1310   ok (!strcmp("System",returnedCF2A.szFaceName),
1311     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1312   
1313   DestroyWindow(hwndRichEdit);
1314 }
1315
1316
1317 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1318                                          LPBYTE pbBuff,
1319                                          LONG cb,
1320                                          LONG *pcb)
1321 {
1322   const char** str = (const char**)dwCookie;
1323   int size = strlen(*str);
1324   if(size > 3)  /* let's make it peice-meal for fun */
1325     size = 3;
1326   *pcb = cb;
1327   if (*pcb > size) {
1328     *pcb = size;
1329   }
1330   if (*pcb > 0) {
1331     memcpy(pbBuff, *str, *pcb);
1332     *str += *pcb;
1333   }
1334   return 0;
1335 }
1336
1337 static void test_EM_GETMODIFY(void)
1338 {
1339   HWND hwndRichEdit = new_richedit(NULL);
1340   LRESULT result;
1341   SETTEXTEX setText;
1342   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1343                        'S', 'o', 'm', 'e', 
1344                        'T', 'e', 'x', 't', 0}; 
1345   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1346                        'S', 'o', 'm', 'e', 
1347                        'O', 't', 'h', 'e', 'r',
1348                        'T', 'e', 'x', 't', 0}; 
1349   const char* streamText = "hello world";
1350   CHARFORMAT2 cf2;
1351   PARAFORMAT2 pf2;
1352   EDITSTREAM es;
1353   
1354   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1355     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1356     FF_DONTCARE, "Courier");
1357   
1358   setText.codepage = 1200;  /* no constant for unicode */
1359   setText.flags = ST_KEEPUNDO;
1360   
1361
1362   /* modify flag shouldn't be set when richedit is first created */
1363   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1364   ok (result == 0, 
1365       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1366   
1367   /* setting modify flag should actually set it */
1368   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1369   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1370   ok (result != 0, 
1371       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1372   
1373   /* clearing modify flag should actually clear it */
1374   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1375   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1376   ok (result == 0, 
1377       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1378  
1379   /* setting font doesn't change modify flag */
1380   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1381   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1382   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1383   ok (result == 0,
1384       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1385
1386   /* setting text should set modify flag */
1387   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1388   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1389   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1390   ok (result != 0,
1391       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1392   
1393   /* undo previous text doesn't reset modify flag */
1394   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1395   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1396   ok (result != 0,
1397       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1398   
1399   /* set text with no flag to keep undo stack should not set modify flag */
1400   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1401   setText.flags = 0;
1402   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1403   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1404   ok (result == 0,
1405       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1406   
1407   /* WM_SETTEXT doesn't modify */
1408   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1409   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1410   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1411   todo_wine {
1412   ok (result == 0,
1413       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1414   }
1415   
1416   /* clear the text */
1417   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1418   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1419   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1420   ok (result == 0,
1421       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1422   
1423   /* replace text */
1424   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1425   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1426   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1427   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1428   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1429   ok (result != 0,
1430       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1431   
1432   /* copy/paste text 1 */
1433   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1434   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1435   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1436   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1437   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1438   ok (result != 0,
1439       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1440   
1441   /* copy/paste text 2 */
1442   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1443   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1444   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1445   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1446   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1447   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1448   ok (result != 0,
1449       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1450   
1451   /* press char */
1452   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1453   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1454   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1455   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1456   ok (result != 0,
1457       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1458
1459   /* press del */
1460   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1461   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1462   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1463   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1464   ok (result != 0,
1465       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1466   
1467   /* set char format */
1468   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1469   cf2.cbSize = sizeof(CHARFORMAT2);
1470   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1471              (LPARAM) &cf2);
1472   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1473   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1474   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1475   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1476   ok (result != 0,
1477       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1478   
1479   /* set para format */
1480   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1481   pf2.cbSize = sizeof(PARAFORMAT2);
1482   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1483              (LPARAM) &pf2);
1484   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1485   pf2.wAlignment = PFA_RIGHT;
1486   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1487   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1488   ok (result == 0,
1489       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1490
1491   /* EM_STREAM */
1492   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1493   es.dwCookie = (DWORD_PTR)&streamText;
1494   es.dwError = 0;
1495   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1496   SendMessage(hwndRichEdit, EM_STREAMIN, 
1497               (WPARAM)(SF_TEXT), (LPARAM)&es);
1498   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1499   ok (result != 0,
1500       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1501
1502   DestroyWindow(hwndRichEdit);
1503 }
1504
1505 struct exsetsel_s {
1506   long min;
1507   long max;
1508   long expected_retval;
1509   int expected_getsel_start;
1510   int expected_getsel_end;
1511   int _exsetsel_todo_wine;
1512   int _getsel_todo_wine;
1513 };
1514
1515 const struct exsetsel_s exsetsel_tests[] = {
1516   /* sanity tests */
1517   {5, 10, 10, 5, 10, 0, 0},
1518   {15, 17, 17, 15, 17, 0, 0},
1519   /* test cpMax > strlen() */
1520   {0, 100, 18, 0, 18, 0, 1},
1521   /* test cpMin == cpMax */
1522   {5, 5, 5, 5, 5, 0, 0},
1523   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1524   {-1, 0, 5, 5, 5, 0, 0},
1525   {-1, 17, 5, 5, 5, 0, 0},
1526   {-1, 18, 5, 5, 5, 0, 0},
1527   /* test cpMin < 0 && cpMax < 0 */
1528   {-1, -1, 17, 17, 17, 0, 0},
1529   {-4, -5, 17, 17, 17, 0, 0},
1530   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1531   {0, -1, 18, 0, 18, 0, 1},
1532   {17, -5, 18, 17, 18, 0, 1},
1533   {18, -3, 17, 17, 17, 0, 0},
1534   /* test if cpMin > cpMax */
1535   {15, 19, 18, 15, 18, 0, 1},
1536   {19, 15, 18, 15, 18, 0, 1}
1537 };
1538
1539 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1540     CHARRANGE cr;
1541     long result;
1542     int start, end;
1543
1544     cr.cpMin = setsel->min;
1545     cr.cpMax = setsel->max;
1546     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1547
1548     if (setsel->_exsetsel_todo_wine) {
1549         todo_wine {
1550             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1551         }
1552     } else {
1553         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1554     }
1555
1556     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1557
1558     if (setsel->_getsel_todo_wine) {
1559         todo_wine {
1560             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);
1561         }
1562     } else {
1563         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);
1564     }
1565 }
1566
1567 static void test_EM_EXSETSEL(void)
1568 {
1569     HWND hwndRichEdit = new_richedit(NULL);
1570     int i;
1571     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1572
1573     /* sending some text to the window */
1574     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1575     /*                                                 01234567890123456*/
1576     /*                                                          10      */
1577
1578     for (i = 0; i < num_tests; i++) {
1579         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1580     }
1581
1582     DestroyWindow(hwndRichEdit);
1583 }
1584
1585 static void test_EM_REPLACESEL(void)
1586 {
1587     HWND hwndRichEdit = new_richedit(NULL);
1588     char buffer[1024] = {0};
1589     int r;
1590
1591     /* sending some text to the window */
1592     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1593     /*                                                 01234567890123456*/
1594     /*                                                          10      */
1595
1596     /* FIXME add more tests */
1597     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1598     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1599     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1600     r = strcmp(buffer, "testing");
1601     ok(0 == r, "expected %d, got %d\n", 0, r);
1602
1603     DestroyWindow(hwndRichEdit);
1604 }
1605
1606 static void test_WM_PASTE(void)
1607 {
1608     int result;
1609     char buffer[1024] = {0};
1610     const char* text1 = "testing paste\r";
1611     const char* text1_after = "testing paste\r\n";
1612     const char* text2 = "testing paste\r\rtesting paste";
1613     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1614     HWND hwndRichEdit = new_richedit(NULL);
1615
1616     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1617     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1618     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1619     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1620     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1621     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1622     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1623     result = strcmp(text1_after, buffer);
1624     ok(result == 0,
1625         "test paste: strcmp = %i\n", result);
1626
1627     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1628     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1629     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1630     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1631     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1632     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1633     SendMessage(hwndRichEdit, WM_CHAR, 25, 0);  /* ctrl-y */
1634     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1635     result = strcmp(buffer,text3);
1636     ok(result == 0,
1637         "test paste: strcmp = %i\n", result);
1638
1639     DestroyWindow(hwndRichEdit);
1640 }
1641
1642 static void test_EM_FORMATRANGE(void)
1643 {
1644   int r;
1645   FORMATRANGE fr;
1646   HDC hdc;
1647   HWND hwndRichEdit = new_richedit(NULL);
1648
1649   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1650
1651   hdc = GetDC(hwndRichEdit);
1652   ok(hdc != NULL, "Could not get HDC\n");
1653
1654   fr.hdc = fr.hdcTarget = hdc;
1655   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1656   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1657   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1658   fr.chrg.cpMin = 0;
1659   fr.chrg.cpMax = 20;
1660
1661   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1662   todo_wine {
1663     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1664   }
1665
1666   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1667   todo_wine {
1668     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1669   }
1670
1671   fr.chrg.cpMin = 0;
1672   fr.chrg.cpMax = 10;
1673
1674   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1675   todo_wine {
1676     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1677   }
1678
1679   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1680   todo_wine {
1681     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1682   }
1683
1684   DestroyWindow(hwndRichEdit);
1685 }
1686
1687 static int nCallbackCount = 0;
1688
1689 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1690                                  LONG cb, LONG* pcb)
1691 {
1692   const char text[] = {'t','e','s','t'};
1693
1694   if (sizeof(text) <= cb)
1695   {
1696     if ((int)dwCookie != nCallbackCount)
1697     {
1698       *pcb = 0;
1699       return 0;
1700     }
1701
1702     memcpy (pbBuff, text, sizeof(text));
1703     *pcb = sizeof(text);
1704
1705     nCallbackCount++;
1706
1707     return 0;
1708   }
1709   else
1710     return 1; /* indicates callback failed */
1711 }
1712
1713 static void test_EM_StreamIn_Undo(void)
1714 {
1715   /* The purpose of this test is to determine when a EM_StreamIn should be
1716    * undoable. This is important because WM_PASTE currently uses StreamIn and
1717    * pasting should always be undoable but streaming isn't always.
1718    *
1719    * cases to test:
1720    * StreamIn plain text without SFF_SELECTION.
1721    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1722    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1723    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1724    * Feel free to add tests for other text modes or StreamIn things.
1725    */
1726
1727
1728   HWND hwndRichEdit = new_richedit(NULL);
1729   LRESULT result;
1730   EDITSTREAM es;
1731   char buffer[1024] = {0};
1732   const char randomtext[] = "Some text";
1733
1734   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1735
1736   /* StreamIn, no SFF_SELECTION */
1737   es.dwCookie = nCallbackCount;
1738   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1739   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1740   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1741   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1742   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1743   result = strcmp (buffer,"test");
1744   ok (result  == 0,
1745       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1746
1747   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1748   ok (result == FALSE,
1749       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1750
1751   /* StreamIn, SFF_SELECTION, but nothing selected */
1752   es.dwCookie = nCallbackCount;
1753   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1754   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1755   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1756   SendMessage(hwndRichEdit, EM_STREAMIN,
1757               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1758   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1759   result = strcmp (buffer,"testSome text");
1760   ok (result  == 0,
1761       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1762
1763   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1764   ok (result == TRUE,
1765      "EM_STREAMIN with SFF_SELECTION but no selection set "
1766       "should create an undo\n");
1767
1768   /* StreamIn, SFF_SELECTION, with a selection */
1769   es.dwCookie = nCallbackCount;
1770   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1771   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1772   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1773   SendMessage(hwndRichEdit, EM_STREAMIN,
1774               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1775   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1776   result = strcmp (buffer,"Sometesttext");
1777   ok (result  == 0,
1778       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1779
1780   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1781   ok (result == TRUE,
1782       "EM_STREAMIN with SFF_SELECTION and selection set "
1783       "should create an undo\n");
1784
1785 }
1786
1787 static BOOL is_em_settextex_supported(HWND hwnd)
1788 {
1789     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1790     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1791 }
1792
1793 static void test_unicode_conversions(void)
1794 {
1795     static const WCHAR tW[] = {'t',0};
1796     static const WCHAR teW[] = {'t','e',0};
1797     static const WCHAR textW[] = {'t','e','s','t',0};
1798     static const char textA[] = "test";
1799     char bufA[64];
1800     WCHAR bufW[64];
1801     HWND hwnd;
1802     int is_win9x, em_settextex_supported, ret;
1803
1804     is_win9x = GetVersion() & 0x80000000;
1805
1806 #define set_textA(hwnd, wm_set_text, txt) \
1807     do { \
1808         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1809         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1810         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1811         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1812         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1813     } while(0)
1814 #define expect_textA(hwnd, wm_get_text, txt) \
1815     do { \
1816         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1817         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1818         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1819         memset(bufA, 0xAA, sizeof(bufA)); \
1820         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1821         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1822         ret = lstrcmpA(bufA, txt); \
1823         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1824     } while(0)
1825
1826 #define set_textW(hwnd, wm_set_text, txt) \
1827     do { \
1828         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1829         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1830         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1831         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1832         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1833     } while(0)
1834 #define expect_textW(hwnd, wm_get_text, txt) \
1835     do { \
1836         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1837         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1838         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1839         memset(bufW, 0xAA, sizeof(bufW)); \
1840         if (is_win9x) \
1841         { \
1842             assert(wm_get_text == EM_GETTEXTEX); \
1843             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1844             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1845         } \
1846         else \
1847         { \
1848             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1849             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1850         } \
1851         ret = lstrcmpW(bufW, txt); \
1852         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1853     } while(0)
1854 #define expect_empty(hwnd, wm_get_text) \
1855     do { \
1856         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1857         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1858         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1859         memset(bufA, 0xAA, sizeof(bufA)); \
1860         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1861         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1862         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1863     } while(0)
1864
1865     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1866                            0, 0, 200, 60, 0, 0, 0, 0);
1867     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1868
1869     ret = IsWindowUnicode(hwnd);
1870     if (is_win9x)
1871         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1872     else
1873         ok(ret, "RichEdit20W should be unicode under NT\n");
1874
1875     /* EM_SETTEXTEX is supported starting from version 3.0 */
1876     em_settextex_supported = is_em_settextex_supported(hwnd);
1877     trace("EM_SETTEXTEX is %ssupported on this platform\n",
1878           em_settextex_supported ? "" : "NOT ");
1879
1880     expect_empty(hwnd, WM_GETTEXT);
1881     expect_empty(hwnd, EM_GETTEXTEX);
1882
1883     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1884     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1885     expect_textA(hwnd, WM_GETTEXT, "t");
1886     expect_textA(hwnd, EM_GETTEXTEX, "t");
1887     expect_textW(hwnd, EM_GETTEXTEX, tW);
1888
1889     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1890     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1891     expect_textA(hwnd, WM_GETTEXT, "te");
1892     expect_textA(hwnd, EM_GETTEXTEX, "te");
1893     expect_textW(hwnd, EM_GETTEXTEX, teW);
1894
1895     set_textA(hwnd, WM_SETTEXT, NULL);
1896     expect_empty(hwnd, WM_GETTEXT);
1897     expect_empty(hwnd, EM_GETTEXTEX);
1898
1899     if (is_win9x)
1900         set_textA(hwnd, WM_SETTEXT, textW);
1901     else
1902         set_textA(hwnd, WM_SETTEXT, textA);
1903     expect_textA(hwnd, WM_GETTEXT, textA);
1904     expect_textA(hwnd, EM_GETTEXTEX, textA);
1905     expect_textW(hwnd, EM_GETTEXTEX, textW);
1906
1907     if (em_settextex_supported)
1908     {
1909         set_textA(hwnd, EM_SETTEXTEX, textA);
1910         expect_textA(hwnd, WM_GETTEXT, textA);
1911         expect_textA(hwnd, EM_GETTEXTEX, textA);
1912         expect_textW(hwnd, EM_GETTEXTEX, textW);
1913     }
1914
1915     if (!is_win9x)
1916     {
1917         set_textW(hwnd, WM_SETTEXT, textW);
1918         expect_textW(hwnd, WM_GETTEXT, textW);
1919         expect_textA(hwnd, WM_GETTEXT, textA);
1920         expect_textW(hwnd, EM_GETTEXTEX, textW);
1921         expect_textA(hwnd, EM_GETTEXTEX, textA);
1922
1923         if (em_settextex_supported)
1924         {
1925             set_textW(hwnd, EM_SETTEXTEX, textW);
1926             expect_textW(hwnd, WM_GETTEXT, textW);
1927             expect_textA(hwnd, WM_GETTEXT, textA);
1928             expect_textW(hwnd, EM_GETTEXTEX, textW);
1929             expect_textA(hwnd, EM_GETTEXTEX, textA);
1930         }
1931     }
1932     DestroyWindow(hwnd);
1933
1934     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1935                            0, 0, 200, 60, 0, 0, 0, 0);
1936     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1937
1938     ret = IsWindowUnicode(hwnd);
1939     ok(!ret, "RichEdit20A should NOT be unicode\n");
1940
1941     set_textA(hwnd, WM_SETTEXT, textA);
1942     expect_textA(hwnd, WM_GETTEXT, textA);
1943     expect_textA(hwnd, EM_GETTEXTEX, textA);
1944     expect_textW(hwnd, EM_GETTEXTEX, textW);
1945
1946     if (em_settextex_supported)
1947     {
1948         set_textA(hwnd, EM_SETTEXTEX, textA);
1949         expect_textA(hwnd, WM_GETTEXT, textA);
1950         expect_textA(hwnd, EM_GETTEXTEX, textA);
1951         expect_textW(hwnd, EM_GETTEXTEX, textW);
1952     }
1953
1954     if (!is_win9x)
1955     {
1956         set_textW(hwnd, WM_SETTEXT, textW);
1957         expect_textW(hwnd, WM_GETTEXT, textW);
1958         expect_textA(hwnd, WM_GETTEXT, textA);
1959         expect_textW(hwnd, EM_GETTEXTEX, textW);
1960         expect_textA(hwnd, EM_GETTEXTEX, textA);
1961
1962         if (em_settextex_supported)
1963         {
1964             set_textW(hwnd, EM_SETTEXTEX, textW);
1965             expect_textW(hwnd, WM_GETTEXT, textW);
1966             expect_textA(hwnd, WM_GETTEXT, textA);
1967             expect_textW(hwnd, EM_GETTEXTEX, textW);
1968             expect_textA(hwnd, EM_GETTEXTEX, textA);
1969         }
1970     }
1971     DestroyWindow(hwnd);
1972 }
1973
1974
1975 static void test_EM_GETTEXTLENGTHEX(void)
1976 {
1977     HWND hwnd;
1978     GETTEXTLENGTHEX gtl;
1979     int ret;
1980
1981     /* single line */
1982     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1983                            0, 0, 200, 60, 0, 0, 0, 0);
1984     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1985
1986     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1987     gtl.codepage = CP_ACP;
1988     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1989     ok(ret == 0, "ret %d\n",ret);
1990
1991     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1992     gtl.codepage = CP_ACP;
1993     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1994     ok(ret == 0, "ret %d\n",ret);
1995
1996     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
1997
1998     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1999     gtl.codepage = CP_ACP;
2000     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2001     todo_wine ok(ret == 1, "ret %d\n",ret);
2002
2003     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2004     gtl.codepage = CP_ACP;
2005     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2006     todo_wine ok(ret == 1, "ret %d\n",ret);
2007
2008     DestroyWindow(hwnd);
2009
2010     /* multi line */
2011     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2012                            0, 0, 200, 60, 0, 0, 0, 0);
2013     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2014
2015     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2016     gtl.codepage = CP_ACP;
2017     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2018     todo_wine ok(ret == 0, "ret %d\n",ret);
2019
2020     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2021     gtl.codepage = CP_ACP;
2022     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2023     ok(ret == 0, "ret %d\n",ret);
2024
2025     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
2026
2027     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2028     gtl.codepage = CP_ACP;
2029     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2030     todo_wine ok(ret == 10, "ret %d\n",ret);
2031
2032     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2033     gtl.codepage = CP_ACP;
2034     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2035     ok(ret == 6, "ret %d\n",ret);
2036
2037     DestroyWindow(hwnd);
2038 }
2039
2040
2041 /* globals that parent and child access when checking event masks & notifications */
2042 static HWND eventMaskEditHwnd = 0;
2043 static int queriedEventMask;
2044 static int watchForEventMask = 0;
2045
2046 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2047 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2048 {
2049     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2050     {
2051       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2052     }
2053     return DefWindowProcA(hwnd, message, wParam, lParam);
2054 }
2055
2056 /* test event masks in combination with WM_COMMAND */
2057 static void test_eventMask(void)
2058 {
2059     HWND parent;
2060     int ret;
2061     WNDCLASSA cls;
2062     const char text[] = "foo bar\n";
2063     int eventMask;
2064
2065     /* register class to capture WM_COMMAND */
2066     cls.style = 0;
2067     cls.lpfnWndProc = ParentMsgCheckProcA;
2068     cls.cbClsExtra = 0;
2069     cls.cbWndExtra = 0;
2070     cls.hInstance = GetModuleHandleA(0);
2071     cls.hIcon = 0;
2072     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2073     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2074     cls.lpszMenuName = NULL;
2075     cls.lpszClassName = "EventMaskParentClass";
2076     if(!RegisterClassA(&cls)) assert(0);
2077
2078     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2079                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
2080     ok (parent != 0, "Failed to create parent window\n");
2081
2082     eventMaskEditHwnd = new_richedit(parent);
2083     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2084
2085     eventMask = ENM_CHANGE | ENM_UPDATE;
2086     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2087     ok(ret == ENM_NONE, "wrong event mask\n");
2088     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2089     ok(ret == eventMask, "failed to set event mask\n");
2090
2091     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2092     queriedEventMask = 0;  /* initialize to something other than we expect */
2093     watchForEventMask = EN_CHANGE;
2094     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2095     ok(ret == TRUE, "failed to set text\n");
2096     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2097        notification in response to WM_SETTEXT */
2098     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2099             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2100
2101 }
2102
2103
2104 START_TEST( editor )
2105 {
2106   MSG msg;
2107   time_t end;
2108
2109   /* Must explicitly LoadLibrary(). The test has no references to functions in
2110    * RICHED20.DLL, so the linker doesn't actually link to it. */
2111   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2112   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2113
2114   test_EM_FINDTEXT();
2115   test_EM_GETLINE();
2116   test_EM_SCROLLCARET();
2117   test_EM_SCROLL();
2118   test_WM_SETTEXT();
2119   test_EM_SETTEXTMODE();
2120   test_TM_PLAINTEXT();
2121   test_EM_SETOPTIONS();
2122   test_WM_GETTEXT();
2123   test_EM_AUTOURLDETECT();
2124   test_EM_SETUNDOLIMIT();
2125   test_ES_PASSWORD();
2126   test_EM_SETTEXTEX();
2127   test_EM_LIMITTEXT();
2128   test_EM_EXLIMITTEXT();
2129   test_EM_GETLIMITTEXT();
2130   test_WM_SETFONT();
2131   test_EM_GETMODIFY();
2132   test_EM_EXSETSEL();
2133   test_WM_PASTE();
2134   test_EM_StreamIn_Undo();
2135   test_EM_FORMATRANGE();
2136   test_unicode_conversions();
2137   test_EM_GETTEXTLENGTHEX();
2138   test_EM_REPLACESEL();
2139   test_eventMask();
2140
2141   /* Set the environment variable WINETEST_RICHED20 to keep windows
2142    * responsive and open for 30 seconds. This is useful for debugging.
2143    *
2144    * The message pump uses PeekMessage() to empty the queue and then sleeps for
2145    * 50ms before retrying the queue. */
2146   end = time(NULL) + 30;
2147   if (getenv( "WINETEST_RICHED20" )) {
2148     while (time(NULL) < end) {
2149       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2150         TranslateMessage(&msg);
2151         DispatchMessage(&msg);
2152       } else {
2153         Sleep(50);
2154       }
2155     }
2156   }
2157
2158   OleFlushClipboard();
2159   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
2160 }