wintrust: Implement WintrustLoadFunctionPointers.
[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 <wine/test.h>
33
34 static HMODULE hmoduleRichEdit;
35
36 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
37   HWND hwnd;
38   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
39                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
40                       hmoduleRichEdit, NULL);
41   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
42   return hwnd;
43 }
44
45 static HWND new_richedit(HWND parent) {
46   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
47 }
48
49 static const char haystack[] = "WINEWine wineWine wine WineWine";
50                              /* ^0        ^10       ^20       ^30 */
51
52 struct find_s {
53   int start;
54   int end;
55   const char *needle;
56   int flags;
57   int expected_loc;
58   int _todo_wine;
59 };
60
61
62 struct find_s find_tests[] = {
63   /* Find in empty text */
64   {0, -1, "foo", FR_DOWN, -1, 0},
65   {0, -1, "foo", 0, -1, 0},
66   {0, -1, "", FR_DOWN, -1, 0},
67   {20, 5, "foo", FR_DOWN, -1, 0},
68   {5, 20, "foo", FR_DOWN, -1, 0}
69 };
70
71 struct find_s find_tests2[] = {
72   /* No-result find */
73   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
74   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
75
76   /* Subsequent finds */
77   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
78   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
79   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
80   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
81
82   /* Find backwards */
83   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
84   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
85   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
86
87   /* Case-insensitive */
88   {1, 31, "wInE", FR_DOWN, 4, 0},
89   {1, 31, "Wine", FR_DOWN, 4, 0},
90
91   /* High-to-low ranges */
92   {20, 5, "Wine", FR_DOWN, -1, 0},
93   {2, 1, "Wine", FR_DOWN, -1, 0},
94   {30, 29, "Wine", FR_DOWN, -1, 0},
95   {20, 5, "Wine", 0, 13, 0},
96
97   /* Find nothing */
98   {5, 10, "", FR_DOWN, -1, 0},
99   {10, 5, "", FR_DOWN, -1, 0},
100   {0, -1, "", FR_DOWN, -1, 0},
101   {10, 5, "", 0, -1, 0},
102
103   /* Whole-word search */
104   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
105   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
106   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
107   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
108   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
109   {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
110   {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
111   
112   /* Bad ranges */
113   {5, 200, "XXX", FR_DOWN, -1, 0},
114   {-20, 20, "Wine", FR_DOWN, -1, 0},
115   {-20, 20, "Wine", FR_DOWN, -1, 0},
116   {-15, -20, "Wine", FR_DOWN, -1, 0},
117   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
118
119   /* Check the case noted in bug 4479 where matches at end aren't recognized */
120   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
121   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
122   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
124   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125
126   /* The backwards case of bug 4479; bounds look right
127    * Fails because backward find is wrong */
128   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
129   {0, 20, "WINE", FR_MATCHCASE, -1, 0}
130 };
131
132 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
133   int findloc;
134   FINDTEXT ft;
135   memset(&ft, 0, sizeof(ft));
136   ft.chrg.cpMin = f->start;
137   ft.chrg.cpMax = f->end;
138   ft.lpstrText = f->needle;
139   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
140   ok(findloc == f->expected_loc,
141      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
142      name, id, f->needle, f->start, f->end, f->flags, findloc);
143 }
144
145 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
146     int id) {
147   int findloc;
148   FINDTEXTEX ft;
149   memset(&ft, 0, sizeof(ft));
150   ft.chrg.cpMin = f->start;
151   ft.chrg.cpMax = f->end;
152   ft.lpstrText = f->needle;
153   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
154   ok(findloc == f->expected_loc,
155       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
156       name, id, f->needle, f->start, f->end, f->flags, findloc);
157   ok(ft.chrgText.cpMin == f->expected_loc,
158       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
159       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
160   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
161         : f->expected_loc + strlen(f->needle)),
162       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
163       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
164 }
165
166 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
167     int num_tests)
168 {
169   int i;
170
171   for (i = 0; i < num_tests; i++) {
172     if (find[i]._todo_wine) {
173       todo_wine {
174         check_EM_FINDTEXT(hwnd, name, &find[i], i);
175         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
176       }
177     } else {
178         check_EM_FINDTEXT(hwnd, name, &find[i], i);
179         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
180     }
181   }
182 }
183
184 static void test_EM_FINDTEXT(void)
185 {
186   HWND hwndRichEdit = new_richedit(NULL);
187
188   /* Empty rich edit control */
189   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
190       sizeof(find_tests)/sizeof(struct find_s));
191
192   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
193
194   /* Haystack text */
195   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
196       sizeof(find_tests2)/sizeof(struct find_s));
197
198   DestroyWindow(hwndRichEdit);
199 }
200
201 static const struct getline_s {
202   int line;
203   size_t buffer_len;
204   const char *text;
205 } gl[] = {
206   {0, 10, "foo bar\r"},
207   {1, 10, "\r"},
208   {2, 10, "bar\r"},
209   {3, 10, "\r"},
210
211   /* Buffer smaller than line length */
212   {0, 2, "foo bar\r"},
213   {0, 1, "foo bar\r"},
214   {0, 0, "foo bar\r"}
215 };
216
217 static void test_EM_GETLINE(void)
218 {
219   int i;
220   HWND hwndRichEdit = new_richedit(NULL);
221   static const int nBuf = 1024;
222   char dest[1024], origdest[1024];
223   const char text[] = "foo bar\n"
224       "\n"
225       "bar\n";
226
227   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
228
229   memset(origdest, 0xBB, nBuf);
230   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
231   {
232     int nCopied;
233     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
234     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
235     memset(dest, 0xBB, nBuf);
236     *(WORD *) dest = gl[i].buffer_len;
237
238     /* EM_GETLINE appends a "\r\0" to the end of the line
239      * nCopied counts up to and including the '\r' */
240     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
241     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
242        expected_nCopied);
243     /* two special cases since a parameter is passed via dest */
244     if (gl[i].buffer_len == 0)
245       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
246          "buffer_len=0\n");
247     else if (gl[i].buffer_len == 1)
248       ok(dest[0] == gl[i].text[0] && !dest[1] &&
249          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
250     else
251     {
252       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
253          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
254       ok(!strncmp(dest + expected_bytes_written, origdest
255                   + expected_bytes_written, nBuf - expected_bytes_written),
256          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
257     }
258   }
259
260   DestroyWindow(hwndRichEdit);
261 }
262
263 static int get_scroll_pos_y(HWND hwnd)
264 {
265   POINT p = {-1, -1};
266   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
267   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
268   return p.y;
269 }
270
271 static void move_cursor(HWND hwnd, long charindex)
272 {
273   CHARRANGE cr;
274   cr.cpMax = charindex;
275   cr.cpMin = charindex;
276   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
277 }
278
279 static void line_scroll(HWND hwnd, int amount)
280 {
281   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
282 }
283
284 static void test_EM_SCROLLCARET(void)
285 {
286   int prevY, curY;
287   HWND hwndRichEdit = new_richedit(NULL);
288   const char text[] = "aa\n"
289       "this is a long line of text that should be longer than the "
290       "control's width\n"
291       "cc\n"
292       "dd\n"
293       "ee\n"
294       "ff\n"
295       "gg\n"
296       "hh\n";
297
298   /* Can't verify this */
299   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
300
301   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
302
303   /* Caret above visible window */
304   line_scroll(hwndRichEdit, 3);
305   prevY = get_scroll_pos_y(hwndRichEdit);
306   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
307   curY = get_scroll_pos_y(hwndRichEdit);
308   ok(prevY != curY, "%d == %d\n", prevY, curY);
309
310   /* Caret below visible window */
311   move_cursor(hwndRichEdit, sizeof(text) - 1);
312   line_scroll(hwndRichEdit, -3);
313   prevY = get_scroll_pos_y(hwndRichEdit);
314   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
315   curY = get_scroll_pos_y(hwndRichEdit);
316   ok(prevY != curY, "%d == %d\n", prevY, curY);
317
318   /* Caret in visible window */
319   move_cursor(hwndRichEdit, sizeof(text) - 2);
320   prevY = get_scroll_pos_y(hwndRichEdit);
321   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
322   curY = get_scroll_pos_y(hwndRichEdit);
323   ok(prevY == curY, "%d != %d\n", prevY, curY);
324
325   /* Caret still in visible window */
326   line_scroll(hwndRichEdit, -1);
327   prevY = get_scroll_pos_y(hwndRichEdit);
328   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
329   curY = get_scroll_pos_y(hwndRichEdit);
330   ok(prevY == curY, "%d != %d\n", prevY, curY);
331
332   DestroyWindow(hwndRichEdit);
333 }
334
335 static void test_EM_SETTEXTMODE(void)
336 {
337   HWND hwndRichEdit = new_richedit(NULL);
338   CHARFORMAT2 cf2, cf2test;
339   CHARRANGE cr;
340   int rc = 0;
341
342   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
343   /*Insert text into the control*/
344
345   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
346
347   /*Attempt to change the control to plain text mode*/
348   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
349   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
350
351   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
352   If rich text is pasted, it should have the same formatting as the rest
353   of the text in the control*/
354
355   /*Italicize the text
356   *NOTE: If the default text was already italicized, the test will simply
357   reverse; in other words, it will copy a regular "wine" into a plain
358   text window that uses an italicized format*/
359   cf2.cbSize = sizeof(CHARFORMAT2);
360   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
361              (LPARAM) &cf2);
362
363   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
364   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
365
366   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
367   however, SCF_ALL has been implemented*/
368   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
369   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
370
371   /*Select the string "wine"*/
372   cr.cpMin = 0;
373   cr.cpMax = 4;
374   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
375
376   /*Copy the italicized "wine" to the clipboard*/
377   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
378
379   /*Reset the formatting to default*/
380   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
381   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
382
383   /*Clear the text in the control*/
384   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
385
386   /*Switch to Plain Text Mode*/
387   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
388   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
389
390   /*Input "wine" again in normal format*/
391   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
392
393   /*Paste the italicized "wine" into the control*/
394   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
395
396   /*Select a character from the first "wine" string*/
397   cr.cpMin = 2;
398   cr.cpMax = 3;
399   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
400
401   /*Retrieve its formatting*/
402   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
403               (LPARAM) &cf2);
404
405   /*Select a character from the second "wine" string*/
406   cr.cpMin = 5;
407   cr.cpMax = 6;
408   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
409
410   /*Retrieve its formatting*/
411   cf2test.cbSize = sizeof(CHARFORMAT2);
412   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
413                (LPARAM) &cf2test);
414
415   /*Compare the two formattings*/
416     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
417       "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
418        cf2.dwEffects, cf2test.dwEffects);
419   /*Test TM_RICHTEXT by: switching back to Rich Text mode
420                          printing "wine" in the current format(normal)
421                          pasting "wine" from the clipboard(italicized)
422                          comparing the two formats(should differ)*/
423
424   /*Attempt to switch with text in control*/
425   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
426   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
427
428   /*Clear control*/
429   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
430
431   /*Switch into Rich Text mode*/
432   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
433   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
434
435   /*Print "wine" in normal formatting into the control*/
436   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
437
438   /*Paste italicized "wine" into the control*/
439   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
440
441   /*Select text from the first "wine" string*/
442   cr.cpMin = 1;
443   cr.cpMax = 3;
444   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
445
446   /*Retrieve its formatting*/
447   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
448                 (LPARAM) &cf2);
449
450   /*Select text from the second "wine" string*/
451   cr.cpMin = 6;
452   cr.cpMax = 7;
453   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
454
455   /*Retrieve its formatting*/
456   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
457                 (LPARAM) &cf2test);
458
459   /*Test that the two formattings are not the same*/
460   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
461       "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
462       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
463
464   DestroyWindow(hwndRichEdit);
465 }
466
467 static void test_TM_PLAINTEXT(void)
468 {
469   /*Tests plain text properties*/
470
471   HWND hwndRichEdit = new_richedit(NULL);
472   CHARFORMAT2 cf2, cf2test;
473   CHARRANGE cr;
474
475   /*Switch to plain text mode*/
476
477   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
478   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
479
480   /*Fill control with text*/
481
482   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
483
484   /*Select some text and bold it*/
485
486   cr.cpMin = 10;
487   cr.cpMax = 20;
488   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
489   cf2.cbSize = sizeof(CHARFORMAT2);
490   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
491               (LPARAM) &cf2);
492
493   cf2.dwMask = CFM_BOLD | cf2.dwMask;
494   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
495
496   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
497
498   /*Get the formatting of those characters*/
499
500   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
501
502   /*Get the formatting of some other characters*/
503   cf2test.cbSize = sizeof(CHARFORMAT2);
504   cr.cpMin = 21;
505   cr.cpMax = 30;
506   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
507   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
508
509   /*Test that they are the same as plain text allows only one formatting*/
510
511   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
512      "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
513      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
514   
515   /*Fill the control with a "wine" string, which when inserted will be bold*/
516
517   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
518
519   /*Copy the bolded "wine" string*/
520
521   cr.cpMin = 0;
522   cr.cpMax = 4;
523   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
524   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
525
526   /*Swap back to rich text*/
527
528   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
529   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
530
531   /*Set the default formatting to bold italics*/
532
533   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
534   cf2.dwMask |= CFM_ITALIC;
535   cf2.dwEffects ^= CFE_ITALIC;
536   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
537
538   /*Set the text in the control to "wine", which will be bold and italicized*/
539
540   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
541
542   /*Paste the plain text "wine" string, which should take the insert
543    formatting, which at the moment is bold italics*/
544
545   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
546
547   /*Select the first "wine" string and retrieve its formatting*/
548
549   cr.cpMin = 1;
550   cr.cpMax = 3;
551   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
552   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
553
554   /*Select the second "wine" string and retrieve its formatting*/
555
556   cr.cpMin = 5;
557   cr.cpMax = 7;
558   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
559   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
560
561   /*Compare the two formattings. They should be the same.*/
562
563   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
564      "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
565      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
566   DestroyWindow(hwndRichEdit);
567 }
568
569 static void test_WM_GETTEXT(void)
570 {
571     HWND hwndRichEdit = new_richedit(NULL);
572     static const char text[] = "Hello. My name is RichEdit!";
573     char buffer[1024] = {0};
574     int result;
575
576     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
577     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
578     result = strcmp(buffer,text);
579     ok(result == 0, 
580         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
581     DestroyWindow(hwndRichEdit);
582 }
583
584 /* FIXME: need to test unimplemented options and robustly test wparam */
585 static void test_EM_SETOPTIONS(void)
586 {
587     HWND hwndRichEdit = new_richedit(NULL);
588     static const char text[] = "Hello. My name is RichEdit!";
589     char buffer[1024] = {0};
590
591     /* NEGATIVE TESTING - NO OPTIONS SET */
592     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
594
595     /* testing no readonly by sending 'a' to the control*/
596     SetFocus(hwndRichEdit);
597     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
598     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
599     ok(buffer[0]=='a', 
600        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
601     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
602
603     /* READONLY - sending 'a' to the control */
604     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
605     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
606     SetFocus(hwndRichEdit);
607     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
608     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
609     ok(buffer[0]==text[0], 
610        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
611
612     DestroyWindow(hwndRichEdit);
613 }
614
615 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
616 {
617   CHARFORMAT2W text_format;
618   int link_present = 0;
619   text_format.cbSize = sizeof(text_format);
620   SendMessage(hwnd, EM_SETSEL, 0, 0);
621   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
622   link_present = text_format.dwEffects & CFE_LINK;
623   if (is_url) 
624   { /* control text is url; should get CFE_LINK */
625         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
626   }
627   else 
628   {
629     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
630   }
631 }
632
633 static HWND new_static_wnd(HWND parent) {
634   return new_window("Static", 0, parent);
635 }
636
637 static void test_EM_AUTOURLDETECT(void)
638 {
639   struct urls_s {
640     const char *text;
641     int is_url;
642   } urls[12] = {
643     {"winehq.org", 0},
644     {"http://www.winehq.org", 1},
645     {"http//winehq.org", 0},
646     {"ww.winehq.org", 0},
647     {"www.winehq.org", 1},
648     {"ftp://192.168.1.1", 1},
649     {"ftp//192.168.1.1", 0},
650     {"mailto:your@email.com", 1},    
651     {"prospero:prosperoserver", 1},
652     {"telnet:test", 1},
653     {"news:newserver", 1},
654     {"wais:waisserver", 1}  
655   };
656
657   int i;
658   int urlRet=-1;
659   HWND hwndRichEdit, parent;
660
661   parent = new_static_wnd(NULL);
662   hwndRichEdit = new_richedit(parent);
663   /* Try and pass EM_AUTOURLDETECT some test wParam values */
664   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
665   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
666   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
667   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
668   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
669   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
670   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
671   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
672   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
673   /* for each url, check the text to see if CFE_LINK effect is present */
674   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
675     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
676     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
677     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
678     check_CFE_LINK_rcvd(hwndRichEdit, 0);
679     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
680     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
681     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
682     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
683   }
684   DestroyWindow(hwndRichEdit);
685   DestroyWindow(parent);
686 }
687
688 static void test_EM_SCROLL(void)
689 {
690   int i, j;
691   int r; /* return value */
692   int expr; /* expected return value */
693   HWND hwndRichEdit = new_richedit(NULL);
694   int y_before, y_after; /* units of lines of text */
695
696   /* test a richedit box containing a single line of text */
697   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
698   expr = 0x00010000;
699   for (i = 0; i < 4; i++) {
700     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
701
702     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
703     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
704     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
705        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
706     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
707        "(i == %d)\n", y_after, i);
708   }
709
710   /*
711    * test a richedit box that will scroll. There are two general
712    * cases: the case without any long lines and the case with a long
713    * line.
714    */
715   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
716     if (i == 0)
717       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
718     else
719       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
720                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
721                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722                   "LONG LINE \nb\nc\nd\ne");
723     for (j = 0; j < 12; j++) /* reset scrol position to top */
724       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
725
726     /* get first visible line */
727     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
728     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
729
730     /* get new current first visible line */
731     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
732
733     ok(((r & 0xffffff00) == 0x00010000) &&
734        ((r & 0x000000ff) != 0x00000000),
735        "EM_SCROLL page down didn't scroll by a small positive number of "
736        "lines (r == 0x%08x)\n", r);
737     ok(y_after > y_before, "EM_SCROLL page down not functioning "
738        "(line %d scrolled to line %d\n", y_before, y_after);
739
740     y_before = y_after;
741     
742     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
743     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
744     ok(((r & 0xffffff00) == 0x0001ff00),
745        "EM_SCROLL page up didn't scroll by a small negative number of lines "
746        "(r == 0x%08x)\n", r);
747     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
748        "%d scrolled to line %d\n", y_before, y_after);
749     
750     y_before = y_after;
751
752     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
753
754     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
755
756     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
757        "(r == 0x%08x)\n", r);
758     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
759        "1 line (%d scrolled to %d)\n", y_before, y_after);
760
761     y_before = y_after;
762
763     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
764
765     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
766
767     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
768        "(r == 0x%08x)\n", r);
769     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
770        "line (%d scrolled to %d)\n", y_before, y_after);
771
772     y_before = y_after;
773
774     r = SendMessage(hwndRichEdit, EM_SCROLL,
775                     SB_LINEUP, 0); /* lineup beyond top */
776
777     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
778
779     ok(r == 0x00010000,
780        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
781     ok(y_before == y_after,
782        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
783
784     y_before = y_after;
785
786     r = SendMessage(hwndRichEdit, EM_SCROLL,
787                     SB_PAGEUP, 0);/*page up beyond top */
788
789     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
790
791     ok(r == 0x00010000,
792        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
793     ok(y_before == y_after,
794        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
795
796     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
797       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
798     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
799     r = SendMessage(hwndRichEdit, EM_SCROLL,
800                     SB_PAGEDOWN, 0); /* page down beyond bot */
801     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
802
803     ok(r == 0x00010000,
804        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
805     ok(y_before == y_after,
806        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
807        y_before, y_after);
808
809     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
810     SendMessage(hwndRichEdit, EM_SCROLL,
811                 SB_LINEDOWN, 0); /* line down beyond bot */
812     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
813     
814     ok(r == 0x00010000,
815        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
816     ok(y_before == y_after,
817        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
818        y_before, y_after);
819   }
820   DestroyWindow(hwndRichEdit);
821 }
822
823 static void test_EM_SETUNDOLIMIT(void)
824 {
825   /* cases we test for:
826    * default behaviour - limiting at 100 undo's 
827    * undo disabled - setting a limit of 0
828    * undo limited -  undo limit set to some to some number, like 2
829    * bad input - sending a negative number should default to 100 undo's */
830  
831   HWND hwndRichEdit = new_richedit(NULL);
832   CHARRANGE cr;
833   int i;
834   int result;
835   
836   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
837   cr.cpMin = 0;
838   cr.cpMax = 1;
839   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
840     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
841       also, multiple pastes don't combine like WM_CHAR would */
842   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
843
844   /* first case - check the default */
845   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
846   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
847     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
848   for (i=0; i<100; i++) /* Undo 100 of them */
849     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
850   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
851      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
852
853   /* second case - cannot undo */
854   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
855   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
856   SendMessage(hwndRichEdit,
857               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
858   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
859      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
860
861   /* third case - set it to an arbitrary number */
862   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
863   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
864   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
865   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
867   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
868   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
869      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
870   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
871   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
872      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
873   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
874   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
875      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
876   
877   /* fourth case - setting negative numbers should default to 100 undos */
878   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
879   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
880   ok (result == 100, 
881       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
882       
883   DestroyWindow(hwndRichEdit);
884 }
885
886 static void test_ES_PASSWORD(void)
887 {
888   /* This isn't hugely testable, so we're just going to run it through it's paces. */
889
890   HWND hwndRichEdit = new_richedit(NULL);
891   WCHAR result;
892
893   /* First, check the default of a regular control */
894   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
895   ok (result == 0,
896         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
897
898   /* Now, set it to something normal */
899   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
900   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
901   ok (result == 120,
902         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
903
904   /* Now, set it to something odd */
905   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
906   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
907   ok (result == 1234,
908         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
909   DestroyWindow(hwndRichEdit);
910 }
911
912 static void test_EM_SETTEXTEX(void)
913 {
914   HWND hwndRichEdit = new_richedit(NULL);
915   SETTEXTEX setText;
916   GETTEXTEX getText;
917   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
918                        'S', 'o', 'm', 'e', 
919                        'T', 'e', 'x', 't', 0}; 
920 #define MAX_BUF_LEN 1024
921   WCHAR buf[MAX_BUF_LEN];
922   int result;
923   CHARRANGE cr;
924
925   setText.codepage = 1200;  /* no constant for unicode */
926   getText.codepage = 1200;  /* no constant for unicode */
927   getText.cb = MAX_BUF_LEN;
928   getText.flags = GT_DEFAULT;
929
930   setText.flags = 0;
931   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
932   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
933   ok(lstrcmpW(buf, TestItem1) == 0,
934       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
935
936   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
937                        (WPARAM)&setText, (LPARAM) NULL);
938   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
939   
940   ok (result == 1, 
941       "EM_SETTEXTEX returned %d, instead of 1\n",result);
942   ok(lstrlenW(buf) == 0,
943       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
944   
945   /* put some text back */
946   setText.flags = 0;
947   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
948   /* select some text */
949   cr.cpMax = 1;
950   cr.cpMin = 3;
951   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
952   /* replace current selection */
953   setText.flags = ST_SELECTION;
954   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
955                        (WPARAM)&setText, (LPARAM) NULL);
956   ok(result == 0,
957       "EM_SETTEXTEX with NULL lParam to replace selection"
958       " with no text should return 0. Got %i\n",
959       result);
960   
961   /* put some text back */
962   setText.flags = 0;
963   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
964   /* select some text */
965   cr.cpMax = 1;
966   cr.cpMin = 3;
967   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
968   /* replace current selection */
969   setText.flags = ST_SELECTION;
970   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
971                        (WPARAM)&setText, (LPARAM) TestItem1);
972   /* get text */
973   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
974   ok(result == lstrlenW(TestItem1),
975       "EM_SETTEXTEX with NULL lParam to replace selection"
976       " with no text should return 0. Got %i\n",
977       result);
978   ok(lstrlenW(buf) == 22,
979       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
980       lstrlenW(buf) );
981
982   DestroyWindow(hwndRichEdit);
983 }
984
985 static void test_EM_LIMITTEXT(void)
986 {
987   int ret;
988
989   HWND hwndRichEdit = new_richedit(NULL);
990
991   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
992    * about setting the length to -1 for multiline edit controls doesn't happen.
993    */
994
995   /* Don't check default gettextlimit case. That's done in other tests */
996
997   /* Set textlimit to 100 */
998   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
999   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1000   ok (ret == 100,
1001       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1002
1003   /* Set textlimit to 0 */
1004   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1005   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1006   ok (ret == 65536,
1007       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1008
1009   /* Set textlimit to -1 */
1010   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1011   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1012   ok (ret == -1,
1013       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1014
1015   /* Set textlimit to -2 */
1016   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1017   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1018   ok (ret == -2,
1019       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1020
1021   DestroyWindow (hwndRichEdit);
1022 }
1023
1024
1025 static void test_EM_EXLIMITTEXT(void)
1026 {
1027   int i, selBegin, selEnd, len1, len2;
1028   int result;
1029   char text[1024 + 1];
1030   char buffer[1024 + 1];
1031   int textlimit = 0; /* multiple of 100 */
1032   HWND hwndRichEdit = new_richedit(NULL);
1033   
1034   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1035   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1036   
1037   textlimit = 256000;
1038   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1039   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1040   /* set higher */
1041   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1042   
1043   textlimit = 1000;
1044   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1045   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1046   /* set lower */
1047   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1048  
1049   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1050   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1051   /* default for WParam = 0 */
1052   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1053  
1054   textlimit = sizeof(text)-1;
1055   memset(text, 'W', textlimit);
1056   text[sizeof(text)-1] = 0;
1057   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1058   /* maxed out text */
1059   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1060   
1061   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1062   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1063   len1 = selEnd - selBegin;
1064   
1065   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1066   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1067   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1068   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1069   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1070   len2 = selEnd - selBegin;
1071   
1072   ok(len1 != len2,
1073     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1074     len1,len2,i);
1075   
1076   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1077   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1078   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1079   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1080   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1081   len1 = selEnd - selBegin;
1082   
1083   ok(len1 != len2,
1084     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1085     len1,len2,i);
1086   
1087   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1088   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1089   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1090   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1091   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1092   len2 = selEnd - selBegin;
1093   
1094   ok(len1 == len2, 
1095     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1096     len1,len2,i);
1097
1098   /* set text up to the limit, select all the text, then add a char */
1099   textlimit = 5;
1100   memset(text, 'W', textlimit);
1101   text[textlimit] = 0;
1102   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1103   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1104   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1105   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1106   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1107   result = strcmp(buffer, "A");
1108   ok(0 == result, "got string = \"%s\"\n", buffer);
1109
1110   DestroyWindow(hwndRichEdit);
1111 }
1112
1113 static void test_EM_GETLIMITTEXT(void)
1114 {
1115   int i;
1116   HWND hwndRichEdit = new_richedit(NULL);
1117
1118   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1119   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1120
1121   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1122   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1123   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1124
1125   DestroyWindow(hwndRichEdit);
1126 }
1127
1128 static void test_WM_SETFONT(void)
1129 {
1130   /* There is no invalid input or error conditions for this function.
1131    * NULL wParam and lParam just fall back to their default values 
1132    * It should be noted that even if you use a gibberish name for your fonts
1133    * here, it will still work because the name is stored. They will display as
1134    * System, but will report their name to be whatever they were created as */
1135   
1136   HWND hwndRichEdit = new_richedit(NULL);
1137   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1138     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1139     FF_DONTCARE, "Marlett");
1140   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1141     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1142     FF_DONTCARE, "MS Sans Serif");
1143   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1144     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1145     FF_DONTCARE, "Courier");
1146   LOGFONTA sentLogFont;
1147   CHARFORMAT2A returnedCF2A;
1148   
1149   returnedCF2A.cbSize = sizeof(returnedCF2A);
1150   
1151   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1152   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1153   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1154
1155   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1156   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1157     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1158     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1159
1160   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1161   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1162   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1163   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1164     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1165     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1166     
1167   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1168   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1169   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1170   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1171     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1172     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1173    
1174   /* This last test is special since we send in NULL. We clear the variables
1175    * and just compare to "System" instead of the sent in font name. */
1176   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1177   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1178   returnedCF2A.cbSize = sizeof(returnedCF2A);
1179   
1180   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1181   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1182   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1183   ok (!strcmp("System",returnedCF2A.szFaceName),
1184     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1185   
1186   DestroyWindow(hwndRichEdit);
1187 }
1188
1189
1190 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1191                                          LPBYTE pbBuff,
1192                                          LONG cb,
1193                                          LONG *pcb)
1194 {
1195   const char** str = (const char**)dwCookie;
1196   int size = strlen(*str);
1197   if(size > 3)  /* let's make it peice-meal for fun */
1198     size = 3;
1199   *pcb = cb;
1200   if (*pcb > size) {
1201     *pcb = size;
1202   }
1203   if (*pcb > 0) {
1204     memcpy(pbBuff, *str, *pcb);
1205     *str += *pcb;
1206   }
1207   return 0;
1208 }
1209
1210 static void test_EM_GETMODIFY(void)
1211 {
1212   HWND hwndRichEdit = new_richedit(NULL);
1213   LRESULT result;
1214   SETTEXTEX setText;
1215   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1216                        'S', 'o', 'm', 'e', 
1217                        'T', 'e', 'x', 't', 0}; 
1218   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1219                        'S', 'o', 'm', 'e', 
1220                        'O', 't', 'h', 'e', 'r',
1221                        'T', 'e', 'x', 't', 0}; 
1222   const char* streamText = "hello world";
1223   CHARFORMAT2 cf2;
1224   PARAFORMAT2 pf2;
1225   EDITSTREAM es;
1226   
1227   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1228     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1229     FF_DONTCARE, "Courier");
1230   
1231   setText.codepage = 1200;  /* no constant for unicode */
1232   setText.flags = ST_KEEPUNDO;
1233   
1234
1235   /* modify flag shouldn't be set when richedit is first created */
1236   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1237   ok (result == 0, 
1238       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1239   
1240   /* setting modify flag should actually set it */
1241   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1242   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1243   ok (result != 0, 
1244       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1245   
1246   /* clearing modify flag should actually clear it */
1247   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1248   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1249   ok (result == 0, 
1250       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1251  
1252   /* setting font doesn't change modify flag */
1253   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1254   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1255   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1256   ok (result == 0,
1257       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1258
1259   /* setting text should set modify flag */
1260   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1261   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1262   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1263   ok (result != 0,
1264       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1265   
1266   /* undo previous text doesn't reset modify flag */
1267   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1268   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1269   ok (result != 0,
1270       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1271   
1272   /* set text with no flag to keep undo stack should not set modify flag */
1273   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1274   setText.flags = 0;
1275   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1276   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1277   ok (result == 0,
1278       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1279   
1280   /* WM_SETTEXT doesn't modify */
1281   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1282   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1283   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1284   todo_wine {
1285   ok (result == 0,
1286       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1287   }
1288   
1289   /* clear the text */
1290   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1291   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1292   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1293   ok (result == 0,
1294       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1295   
1296   /* replace text */
1297   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1298   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1299   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1300   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1301   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1302   ok (result != 0,
1303       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1304   
1305   /* copy/paste text 1 */
1306   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1307   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1308   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1309   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1310   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1311   ok (result != 0,
1312       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1313   
1314   /* copy/paste text 2 */
1315   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1316   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1317   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1318   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1319   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1320   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1321   ok (result != 0,
1322       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1323   
1324   /* press char */
1325   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1326   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1327   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1328   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1329   ok (result != 0,
1330       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1331
1332   /* press del */
1333   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1334   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1335   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1336   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1337   ok (result != 0,
1338       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1339   
1340   /* set char format */
1341   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1342   cf2.cbSize = sizeof(CHARFORMAT2);
1343   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1344              (LPARAM) &cf2);
1345   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1346   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1347   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1348   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1349   ok (result != 0,
1350       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1351   
1352   /* set para format */
1353   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1354   pf2.cbSize = sizeof(PARAFORMAT2);
1355   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1356              (LPARAM) &pf2);
1357   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1358   pf2.wAlignment = PFA_RIGHT;
1359   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1360   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1361   ok (result == 0,
1362       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1363
1364   /* EM_STREAM */
1365   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1366   es.dwCookie = (DWORD_PTR)&streamText;
1367   es.dwError = 0;
1368   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1369   SendMessage(hwndRichEdit, EM_STREAMIN, 
1370               (WPARAM)(SF_TEXT), (LPARAM)&es);
1371   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1372   ok (result != 0,
1373       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1374
1375   DestroyWindow(hwndRichEdit);
1376 }
1377
1378 struct exsetsel_s {
1379   long min;
1380   long max;
1381   long expected_retval;
1382   int expected_getsel_start;
1383   int expected_getsel_end;
1384   int _exsetsel_todo_wine;
1385   int _getsel_todo_wine;
1386 };
1387
1388 const struct exsetsel_s exsetsel_tests[] = {
1389   /* sanity tests */
1390   {5, 10, 10, 5, 10, 0, 0},
1391   {15, 17, 17, 15, 17, 0, 0},
1392   /* test cpMax > strlen() */
1393   {0, 100, 18, 0, 18, 0, 1},
1394   /* test cpMin == cpMax */
1395   {5, 5, 5, 5, 5, 0, 0},
1396   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1397   {-1, 0, 5, 5, 5, 0, 0},
1398   {-1, 17, 5, 5, 5, 0, 0},
1399   {-1, 18, 5, 5, 5, 0, 0},
1400   /* test cpMin < 0 && cpMax < 0 */
1401   {-1, -1, 17, 17, 17, 0, 0},
1402   {-4, -5, 17, 17, 17, 0, 0},
1403   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1404   {0, -1, 18, 0, 18, 0, 1},
1405   {17, -5, 18, 17, 18, 0, 1},
1406   {18, -3, 17, 17, 17, 0, 0},
1407   /* test if cpMin > cpMax */
1408   {15, 19, 18, 15, 18, 0, 1},
1409   {19, 15, 18, 15, 18, 0, 1}
1410 };
1411
1412 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1413     CHARRANGE cr;
1414     long result;
1415     int start, end;
1416
1417     cr.cpMin = setsel->min;
1418     cr.cpMax = setsel->max;
1419     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1420
1421     if (setsel->_exsetsel_todo_wine) {
1422         todo_wine {
1423             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1424         }
1425     } else {
1426         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1427     }
1428
1429     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1430
1431     if (setsel->_getsel_todo_wine) {
1432         todo_wine {
1433             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);
1434         }
1435     } else {
1436         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);
1437     }
1438 }
1439
1440 static void test_EM_EXSETSEL(void)
1441 {
1442     HWND hwndRichEdit = new_richedit(NULL);
1443     int i;
1444     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1445
1446     /* sending some text to the window */
1447     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1448     /*                                                 01234567890123456*/
1449     /*                                                          10      */
1450
1451     for (i = 0; i < num_tests; i++) {
1452         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1453     }
1454
1455     DestroyWindow(hwndRichEdit);
1456 }
1457
1458 static void test_WM_PASTE(void)
1459 {
1460     int result;
1461     char buffer[1024] = {0};
1462     const char* text1 = "testing paste\r";
1463     const char* text2 = "testing paste\r\rtesting paste";
1464     const char* text3 = "testing paste\rpaste\rtesting paste";
1465     HWND hwndRichEdit = new_richedit(NULL);
1466
1467     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1468     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1469     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1470     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1471     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1472     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1473     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1474     result = strcmp(text1, buffer);
1475     ok(result == 0,
1476         "test paste: strcmp = %i\n", result);
1477
1478     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1479     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1480     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1481     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1482     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1483     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1484     SendMessage(hwndRichEdit, WM_CHAR, 25, 0);  /* ctrl-y */
1485     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1486     result = strcmp(buffer,text3);
1487     ok(result == 0,
1488         "test paste: strcmp = %i\n", result);
1489
1490     DestroyWindow(hwndRichEdit);
1491 }
1492
1493 static int nCallbackCount = 0;
1494
1495 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1496                                  LONG cb, LONG* pcb)
1497 {
1498   const char text[] = {'t','e','s','t'};
1499
1500   if (sizeof(text) <= cb)
1501   {
1502     if ((int)dwCookie != nCallbackCount)
1503     {
1504       *pcb = 0;
1505       return 0;
1506     }
1507
1508     memcpy (pbBuff, text, sizeof(text));
1509     *pcb = sizeof(text);
1510
1511     nCallbackCount++;
1512
1513     return 0;
1514   }
1515   else
1516     return 1; /* indicates callback failed */
1517 }
1518
1519 static void test_EM_StreamIn_Undo(void)
1520 {
1521   /* The purpose of this test is to determine when a EM_StreamIn should be
1522    * undoable. This is important because WM_PASTE currently uses StreamIn and
1523    * pasting should always be undoable but streaming isn't always.
1524    *
1525    * cases to test:
1526    * StreamIn plain text without SFF_SELECTION.
1527    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1528    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1529    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1530    * Feel free to add tests for other text modes or StreamIn things.
1531    */
1532
1533
1534   HWND hwndRichEdit = new_richedit(NULL);
1535   LRESULT result;
1536   EDITSTREAM es;
1537   char buffer[1024] = {0};
1538   const char randomtext[] = "Some text";
1539
1540   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1541
1542   /* StreamIn, no SFF_SELECTION */
1543   es.dwCookie = nCallbackCount;
1544   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1545   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1546   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1547   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1548   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1549   result = strcmp (buffer,"test");
1550   ok (result  == 0,
1551       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1552
1553   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1554   ok (result == FALSE,
1555       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1556
1557   /* StreamIn, SFF_SELECTION, but nothing selected */
1558   es.dwCookie = nCallbackCount;
1559   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1560   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1561   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1562   SendMessage(hwndRichEdit, EM_STREAMIN,
1563               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1564   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1565   result = strcmp (buffer,"testSome text");
1566   ok (result  == 0,
1567       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1568
1569   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1570   ok (result == TRUE,
1571      "EM_STREAMIN with SFF_SELECTION but no selection set "
1572       "should create an undo\n");
1573
1574   /* StreamIn, SFF_SELECTION, with a selection */
1575   es.dwCookie = nCallbackCount;
1576   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1577   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1578   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1579   SendMessage(hwndRichEdit, EM_STREAMIN,
1580               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1581   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1582   result = strcmp (buffer,"Sometesttext");
1583   ok (result  == 0,
1584       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1585
1586   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1587   ok (result == TRUE,
1588       "EM_STREAMIN with SFF_SELECTION and selection set "
1589       "should create an undo\n");
1590
1591 }
1592
1593 static BOOL is_em_settextex_supported(HWND hwnd)
1594 {
1595     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1596     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1597 }
1598
1599 static void test_unicode_conversions(void)
1600 {
1601     static const WCHAR tW[] = {'t',0};
1602     static const WCHAR teW[] = {'t','e',0};
1603     static const WCHAR textW[] = {'t','e','s','t',0};
1604     static const char textA[] = "test";
1605     char bufA[64];
1606     WCHAR bufW[64];
1607     HWND hwnd;
1608     int is_win9x, em_settextex_supported, ret;
1609
1610     is_win9x = GetVersion() & 0x80000000;
1611
1612 #define set_textA(hwnd, wm_set_text, txt) \
1613     do { \
1614         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1615         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1616         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1617         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1618         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1619     } while(0)
1620 #define expect_textA(hwnd, wm_get_text, txt) \
1621     do { \
1622         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1623         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1624         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1625         memset(bufA, 0xAA, sizeof(bufA)); \
1626         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1627         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1628         ret = lstrcmpA(bufA, txt); \
1629         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1630     } while(0)
1631
1632 #define set_textW(hwnd, wm_set_text, txt) \
1633     do { \
1634         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1635         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1636         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1637         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1638         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1639     } while(0)
1640 #define expect_textW(hwnd, wm_get_text, txt) \
1641     do { \
1642         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1643         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1644         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1645         memset(bufW, 0xAA, sizeof(bufW)); \
1646         if (is_win9x) \
1647         { \
1648             assert(wm_get_text == EM_GETTEXTEX); \
1649             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1650             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1651         } \
1652         else \
1653         { \
1654             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1655             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1656         } \
1657         ret = lstrcmpW(bufW, txt); \
1658         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1659     } while(0)
1660 #define expect_empty(hwnd, wm_get_text) \
1661     do { \
1662         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1663         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1664         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1665         memset(bufA, 0xAA, sizeof(bufA)); \
1666         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1667         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1668         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1669     } while(0)
1670
1671     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1672                            0, 0, 200, 60, 0, 0, 0, 0);
1673     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1674
1675     ret = IsWindowUnicode(hwnd);
1676     if (is_win9x)
1677         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1678     else
1679         ok(ret, "RichEdit20W should be unicode under NT\n");
1680
1681     /* EM_SETTEXTEX is supported starting from version 3.0 */
1682     em_settextex_supported = is_em_settextex_supported(hwnd);
1683     trace("EM_SETTEXTEX is %ssupported on this platform\n",
1684           em_settextex_supported ? "" : "NOT ");
1685
1686     expect_empty(hwnd, WM_GETTEXT);
1687     expect_empty(hwnd, EM_GETTEXTEX);
1688
1689     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1690     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1691     expect_textA(hwnd, WM_GETTEXT, "t");
1692     expect_textA(hwnd, EM_GETTEXTEX, "t");
1693     expect_textW(hwnd, EM_GETTEXTEX, tW);
1694
1695     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1696     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1697     expect_textA(hwnd, WM_GETTEXT, "te");
1698     expect_textA(hwnd, EM_GETTEXTEX, "te");
1699     expect_textW(hwnd, EM_GETTEXTEX, teW);
1700
1701     set_textA(hwnd, WM_SETTEXT, NULL);
1702     expect_empty(hwnd, WM_GETTEXT);
1703     expect_empty(hwnd, EM_GETTEXTEX);
1704
1705     if (is_win9x)
1706         set_textA(hwnd, WM_SETTEXT, textW);
1707     else
1708         set_textA(hwnd, WM_SETTEXT, textA);
1709     expect_textA(hwnd, WM_GETTEXT, textA);
1710     expect_textA(hwnd, EM_GETTEXTEX, textA);
1711     expect_textW(hwnd, EM_GETTEXTEX, textW);
1712
1713     if (em_settextex_supported)
1714     {
1715         set_textA(hwnd, EM_SETTEXTEX, textA);
1716         expect_textA(hwnd, WM_GETTEXT, textA);
1717         expect_textA(hwnd, EM_GETTEXTEX, textA);
1718         expect_textW(hwnd, EM_GETTEXTEX, textW);
1719     }
1720
1721     if (!is_win9x)
1722     {
1723         set_textW(hwnd, WM_SETTEXT, textW);
1724         expect_textW(hwnd, WM_GETTEXT, textW);
1725         expect_textA(hwnd, WM_GETTEXT, textA);
1726         expect_textW(hwnd, EM_GETTEXTEX, textW);
1727         expect_textA(hwnd, EM_GETTEXTEX, textA);
1728
1729         if (em_settextex_supported)
1730         {
1731             set_textW(hwnd, EM_SETTEXTEX, textW);
1732             expect_textW(hwnd, WM_GETTEXT, textW);
1733             expect_textA(hwnd, WM_GETTEXT, textA);
1734             expect_textW(hwnd, EM_GETTEXTEX, textW);
1735             expect_textA(hwnd, EM_GETTEXTEX, textA);
1736         }
1737     }
1738     DestroyWindow(hwnd);
1739
1740     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1741                            0, 0, 200, 60, 0, 0, 0, 0);
1742     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1743
1744     ret = IsWindowUnicode(hwnd);
1745     ok(!ret, "RichEdit20A should NOT be unicode\n");
1746
1747     set_textA(hwnd, WM_SETTEXT, textA);
1748     expect_textA(hwnd, WM_GETTEXT, textA);
1749     expect_textA(hwnd, EM_GETTEXTEX, textA);
1750     expect_textW(hwnd, EM_GETTEXTEX, textW);
1751
1752     if (em_settextex_supported)
1753     {
1754         set_textA(hwnd, EM_SETTEXTEX, textA);
1755         expect_textA(hwnd, WM_GETTEXT, textA);
1756         expect_textA(hwnd, EM_GETTEXTEX, textA);
1757         expect_textW(hwnd, EM_GETTEXTEX, textW);
1758     }
1759
1760     if (!is_win9x)
1761     {
1762         set_textW(hwnd, WM_SETTEXT, textW);
1763         expect_textW(hwnd, WM_GETTEXT, textW);
1764         expect_textA(hwnd, WM_GETTEXT, textA);
1765         expect_textW(hwnd, EM_GETTEXTEX, textW);
1766         expect_textA(hwnd, EM_GETTEXTEX, textA);
1767
1768         if (em_settextex_supported)
1769         {
1770             set_textW(hwnd, EM_SETTEXTEX, textW);
1771             expect_textW(hwnd, WM_GETTEXT, textW);
1772             expect_textA(hwnd, WM_GETTEXT, textA);
1773             expect_textW(hwnd, EM_GETTEXTEX, textW);
1774             expect_textA(hwnd, EM_GETTEXTEX, textA);
1775         }
1776     }
1777     DestroyWindow(hwnd);
1778 }
1779
1780 START_TEST( editor )
1781 {
1782   MSG msg;
1783   time_t end;
1784
1785   /* Must explicitly LoadLibrary(). The test has no references to functions in
1786    * RICHED20.DLL, so the linker doesn't actually link to it. */
1787   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1788   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1789
1790   test_EM_FINDTEXT();
1791   test_EM_GETLINE();
1792   test_EM_SCROLLCARET();
1793   test_EM_SCROLL();
1794   test_EM_SETTEXTMODE();
1795   test_TM_PLAINTEXT();
1796   test_EM_SETOPTIONS();
1797   test_WM_GETTEXT();
1798   test_EM_AUTOURLDETECT();
1799   test_EM_SETUNDOLIMIT();
1800   test_ES_PASSWORD();
1801   test_EM_SETTEXTEX();
1802   test_EM_LIMITTEXT();
1803   test_EM_EXLIMITTEXT();
1804   test_EM_GETLIMITTEXT();
1805   test_WM_SETFONT();
1806   test_EM_GETMODIFY();
1807   test_EM_EXSETSEL();
1808   test_WM_PASTE();
1809   test_EM_StreamIn_Undo();
1810   test_unicode_conversions();
1811
1812   /* Set the environment variable WINETEST_RICHED20 to keep windows
1813    * responsive and open for 30 seconds. This is useful for debugging.
1814    *
1815    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1816    * 50ms before retrying the queue. */
1817   end = time(NULL) + 30;
1818   if (getenv( "WINETEST_RICHED20" )) {
1819     while (time(NULL) < end) {
1820       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1821         TranslateMessage(&msg);
1822         DispatchMessage(&msg);
1823       } else {
1824         Sleep(50);
1825       }
1826     }
1827   }
1828
1829   OleFlushClipboard();
1830   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1831 }