mcicda: Exclude unused headers.
[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 it's 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_EM_SETTEXTEX(void)
914 {
915   HWND hwndRichEdit = new_richedit(NULL);
916   SETTEXTEX setText;
917   GETTEXTEX getText;
918   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
919                        'S', 'o', 'm', 'e', 
920                        'T', 'e', 'x', 't', 0}; 
921 #define MAX_BUF_LEN 1024
922   WCHAR buf[MAX_BUF_LEN];
923   int result;
924   CHARRANGE cr;
925
926   setText.codepage = 1200;  /* no constant for unicode */
927   getText.codepage = 1200;  /* no constant for unicode */
928   getText.cb = MAX_BUF_LEN;
929   getText.flags = GT_DEFAULT;
930
931   setText.flags = 0;
932   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
933   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
934   ok(lstrcmpW(buf, TestItem1) == 0,
935       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
936
937   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
938                        (WPARAM)&setText, (LPARAM) NULL);
939   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
940   
941   ok (result == 1, 
942       "EM_SETTEXTEX returned %d, instead of 1\n",result);
943   ok(lstrlenW(buf) == 0,
944       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
945   
946   /* put some text back */
947   setText.flags = 0;
948   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
949   /* select some text */
950   cr.cpMax = 1;
951   cr.cpMin = 3;
952   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
953   /* replace current selection */
954   setText.flags = ST_SELECTION;
955   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
956                        (WPARAM)&setText, (LPARAM) NULL);
957   ok(result == 0,
958       "EM_SETTEXTEX with NULL lParam to replace selection"
959       " with no text should return 0. Got %i\n",
960       result);
961   
962   /* put some text back */
963   setText.flags = 0;
964   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
965   /* select some text */
966   cr.cpMax = 1;
967   cr.cpMin = 3;
968   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
969   /* replace current selection */
970   setText.flags = ST_SELECTION;
971   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
972                        (WPARAM)&setText, (LPARAM) TestItem1);
973   /* get text */
974   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
975   ok(result == lstrlenW(TestItem1),
976       "EM_SETTEXTEX with NULL lParam to replace selection"
977       " with no text should return 0. Got %i\n",
978       result);
979   ok(lstrlenW(buf) == 22,
980       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
981       lstrlenW(buf) );
982
983   DestroyWindow(hwndRichEdit);
984 }
985
986 static void test_EM_LIMITTEXT(void)
987 {
988   int ret;
989
990   HWND hwndRichEdit = new_richedit(NULL);
991
992   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
993    * about setting the length to -1 for multiline edit controls doesn't happen.
994    */
995
996   /* Don't check default gettextlimit case. That's done in other tests */
997
998   /* Set textlimit to 100 */
999   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1000   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1001   ok (ret == 100,
1002       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1003
1004   /* Set textlimit to 0 */
1005   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1006   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1007   ok (ret == 65536,
1008       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1009
1010   /* Set textlimit to -1 */
1011   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1012   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1013   ok (ret == -1,
1014       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1015
1016   /* Set textlimit to -2 */
1017   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1018   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1019   ok (ret == -2,
1020       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1021
1022   DestroyWindow (hwndRichEdit);
1023 }
1024
1025
1026 static void test_EM_EXLIMITTEXT(void)
1027 {
1028   int i, selBegin, selEnd, len1, len2;
1029   int result;
1030   char text[1024 + 1];
1031   char buffer[1024 + 1];
1032   int textlimit = 0; /* multiple of 100 */
1033   HWND hwndRichEdit = new_richedit(NULL);
1034   
1035   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1036   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1037   
1038   textlimit = 256000;
1039   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1040   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1041   /* set higher */
1042   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1043   
1044   textlimit = 1000;
1045   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1046   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1047   /* set lower */
1048   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1049  
1050   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1051   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1052   /* default for WParam = 0 */
1053   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1054  
1055   textlimit = sizeof(text)-1;
1056   memset(text, 'W', textlimit);
1057   text[sizeof(text)-1] = 0;
1058   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1059   /* maxed out text */
1060   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1061   
1062   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1063   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1064   len1 = selEnd - selBegin;
1065   
1066   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1067   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1068   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1069   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1070   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1071   len2 = selEnd - selBegin;
1072   
1073   ok(len1 != len2,
1074     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1075     len1,len2,i);
1076   
1077   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1078   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1079   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1080   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1081   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1082   len1 = selEnd - selBegin;
1083   
1084   ok(len1 != len2,
1085     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1086     len1,len2,i);
1087   
1088   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1089   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1090   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1091   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1092   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1093   len2 = selEnd - selBegin;
1094   
1095   ok(len1 == len2, 
1096     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1097     len1,len2,i);
1098
1099   /* set text up to the limit, select all the text, then add a char */
1100   textlimit = 5;
1101   memset(text, 'W', textlimit);
1102   text[textlimit] = 0;
1103   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1104   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1105   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1106   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1107   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1108   result = strcmp(buffer, "A");
1109   ok(0 == result, "got string = \"%s\"\n", buffer);
1110
1111   DestroyWindow(hwndRichEdit);
1112 }
1113
1114 static void test_EM_GETLIMITTEXT(void)
1115 {
1116   int i;
1117   HWND hwndRichEdit = new_richedit(NULL);
1118
1119   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1120   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1121
1122   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1123   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1124   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1125
1126   DestroyWindow(hwndRichEdit);
1127 }
1128
1129 static void test_WM_SETFONT(void)
1130 {
1131   /* There is no invalid input or error conditions for this function.
1132    * NULL wParam and lParam just fall back to their default values 
1133    * It should be noted that even if you use a gibberish name for your fonts
1134    * here, it will still work because the name is stored. They will display as
1135    * System, but will report their name to be whatever they were created as */
1136   
1137   HWND hwndRichEdit = new_richedit(NULL);
1138   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1139     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1140     FF_DONTCARE, "Marlett");
1141   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1142     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1143     FF_DONTCARE, "MS Sans Serif");
1144   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1145     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1146     FF_DONTCARE, "Courier");
1147   LOGFONTA sentLogFont;
1148   CHARFORMAT2A returnedCF2A;
1149   
1150   returnedCF2A.cbSize = sizeof(returnedCF2A);
1151   
1152   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1153   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1154   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1155
1156   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1157   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1158     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1159     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1160
1161   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1162   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1163   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1164   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1165     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1166     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1167     
1168   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1169   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1170   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1171   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1172     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1173     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1174    
1175   /* This last test is special since we send in NULL. We clear the variables
1176    * and just compare to "System" instead of the sent in font name. */
1177   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1178   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1179   returnedCF2A.cbSize = sizeof(returnedCF2A);
1180   
1181   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1182   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1183   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1184   ok (!strcmp("System",returnedCF2A.szFaceName),
1185     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1186   
1187   DestroyWindow(hwndRichEdit);
1188 }
1189
1190
1191 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1192                                          LPBYTE pbBuff,
1193                                          LONG cb,
1194                                          LONG *pcb)
1195 {
1196   const char** str = (const char**)dwCookie;
1197   int size = strlen(*str);
1198   if(size > 3)  /* let's make it peice-meal for fun */
1199     size = 3;
1200   *pcb = cb;
1201   if (*pcb > size) {
1202     *pcb = size;
1203   }
1204   if (*pcb > 0) {
1205     memcpy(pbBuff, *str, *pcb);
1206     *str += *pcb;
1207   }
1208   return 0;
1209 }
1210
1211 static void test_EM_GETMODIFY(void)
1212 {
1213   HWND hwndRichEdit = new_richedit(NULL);
1214   LRESULT result;
1215   SETTEXTEX setText;
1216   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1217                        'S', 'o', 'm', 'e', 
1218                        'T', 'e', 'x', 't', 0}; 
1219   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1220                        'S', 'o', 'm', 'e', 
1221                        'O', 't', 'h', 'e', 'r',
1222                        'T', 'e', 'x', 't', 0}; 
1223   const char* streamText = "hello world";
1224   CHARFORMAT2 cf2;
1225   PARAFORMAT2 pf2;
1226   EDITSTREAM es;
1227   
1228   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1229     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1230     FF_DONTCARE, "Courier");
1231   
1232   setText.codepage = 1200;  /* no constant for unicode */
1233   setText.flags = ST_KEEPUNDO;
1234   
1235
1236   /* modify flag shouldn't be set when richedit is first created */
1237   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1238   ok (result == 0, 
1239       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1240   
1241   /* setting modify flag should actually set it */
1242   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1243   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1244   ok (result != 0, 
1245       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1246   
1247   /* clearing modify flag should actually clear it */
1248   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1249   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1250   ok (result == 0, 
1251       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1252  
1253   /* setting font doesn't change modify flag */
1254   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1255   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1256   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1257   ok (result == 0,
1258       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1259
1260   /* setting text should set modify flag */
1261   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1262   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1263   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1264   ok (result != 0,
1265       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1266   
1267   /* undo previous text doesn't reset modify flag */
1268   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1269   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1270   ok (result != 0,
1271       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1272   
1273   /* set text with no flag to keep undo stack should not set modify flag */
1274   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1275   setText.flags = 0;
1276   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1277   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1278   ok (result == 0,
1279       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1280   
1281   /* WM_SETTEXT doesn't modify */
1282   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1283   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1284   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1285   todo_wine {
1286   ok (result == 0,
1287       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1288   }
1289   
1290   /* clear the text */
1291   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1292   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1293   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1294   ok (result == 0,
1295       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1296   
1297   /* replace text */
1298   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1299   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1300   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1301   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1302   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1303   ok (result != 0,
1304       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1305   
1306   /* copy/paste text 1 */
1307   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1308   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1309   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1310   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1311   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1312   ok (result != 0,
1313       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1314   
1315   /* copy/paste text 2 */
1316   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1317   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1318   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1319   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1320   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1321   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1322   ok (result != 0,
1323       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1324   
1325   /* press char */
1326   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1327   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1328   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1329   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1330   ok (result != 0,
1331       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1332
1333   /* press del */
1334   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1335   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1336   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1337   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1338   ok (result != 0,
1339       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1340   
1341   /* set char format */
1342   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1343   cf2.cbSize = sizeof(CHARFORMAT2);
1344   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1345              (LPARAM) &cf2);
1346   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1347   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1348   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1349   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1350   ok (result != 0,
1351       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1352   
1353   /* set para format */
1354   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1355   pf2.cbSize = sizeof(PARAFORMAT2);
1356   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1357              (LPARAM) &pf2);
1358   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1359   pf2.wAlignment = PFA_RIGHT;
1360   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1361   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1362   ok (result == 0,
1363       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1364
1365   /* EM_STREAM */
1366   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1367   es.dwCookie = (DWORD_PTR)&streamText;
1368   es.dwError = 0;
1369   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1370   SendMessage(hwndRichEdit, EM_STREAMIN, 
1371               (WPARAM)(SF_TEXT), (LPARAM)&es);
1372   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1373   ok (result != 0,
1374       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1375
1376   DestroyWindow(hwndRichEdit);
1377 }
1378
1379 struct exsetsel_s {
1380   long min;
1381   long max;
1382   long expected_retval;
1383   int expected_getsel_start;
1384   int expected_getsel_end;
1385   int _exsetsel_todo_wine;
1386   int _getsel_todo_wine;
1387 };
1388
1389 const struct exsetsel_s exsetsel_tests[] = {
1390   /* sanity tests */
1391   {5, 10, 10, 5, 10, 0, 0},
1392   {15, 17, 17, 15, 17, 0, 0},
1393   /* test cpMax > strlen() */
1394   {0, 100, 18, 0, 18, 0, 1},
1395   /* test cpMin == cpMax */
1396   {5, 5, 5, 5, 5, 0, 0},
1397   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1398   {-1, 0, 5, 5, 5, 0, 0},
1399   {-1, 17, 5, 5, 5, 0, 0},
1400   {-1, 18, 5, 5, 5, 0, 0},
1401   /* test cpMin < 0 && cpMax < 0 */
1402   {-1, -1, 17, 17, 17, 0, 0},
1403   {-4, -5, 17, 17, 17, 0, 0},
1404   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1405   {0, -1, 18, 0, 18, 0, 1},
1406   {17, -5, 18, 17, 18, 0, 1},
1407   {18, -3, 17, 17, 17, 0, 0},
1408   /* test if cpMin > cpMax */
1409   {15, 19, 18, 15, 18, 0, 1},
1410   {19, 15, 18, 15, 18, 0, 1}
1411 };
1412
1413 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1414     CHARRANGE cr;
1415     long result;
1416     int start, end;
1417
1418     cr.cpMin = setsel->min;
1419     cr.cpMax = setsel->max;
1420     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1421
1422     if (setsel->_exsetsel_todo_wine) {
1423         todo_wine {
1424             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1425         }
1426     } else {
1427         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1428     }
1429
1430     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1431
1432     if (setsel->_getsel_todo_wine) {
1433         todo_wine {
1434             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);
1435         }
1436     } else {
1437         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);
1438     }
1439 }
1440
1441 static void test_EM_EXSETSEL(void)
1442 {
1443     HWND hwndRichEdit = new_richedit(NULL);
1444     int i;
1445     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1446
1447     /* sending some text to the window */
1448     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1449     /*                                                 01234567890123456*/
1450     /*                                                          10      */
1451
1452     for (i = 0; i < num_tests; i++) {
1453         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1454     }
1455
1456     DestroyWindow(hwndRichEdit);
1457 }
1458
1459 static void test_WM_PASTE(void)
1460 {
1461     int result;
1462     char buffer[1024] = {0};
1463     const char* text1 = "testing paste\r";
1464     const char* text2 = "testing paste\r\rtesting paste";
1465     const char* text3 = "testing paste\rpaste\rtesting paste";
1466     HWND hwndRichEdit = new_richedit(NULL);
1467
1468     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1469     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1470     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1471     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1472     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1473     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1474     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1475     result = strcmp(text1, buffer);
1476     ok(result == 0,
1477         "test paste: strcmp = %i\n", result);
1478
1479     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1480     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1481     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1482     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1483     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1484     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1485     SendMessage(hwndRichEdit, WM_CHAR, 25, 0);  /* ctrl-y */
1486     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1487     result = strcmp(buffer,text3);
1488     ok(result == 0,
1489         "test paste: strcmp = %i\n", result);
1490
1491     DestroyWindow(hwndRichEdit);
1492 }
1493
1494 static int nCallbackCount = 0;
1495
1496 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1497                                  LONG cb, LONG* pcb)
1498 {
1499   const char text[] = {'t','e','s','t'};
1500
1501   if (sizeof(text) <= cb)
1502   {
1503     if ((int)dwCookie != nCallbackCount)
1504     {
1505       *pcb = 0;
1506       return 0;
1507     }
1508
1509     memcpy (pbBuff, text, sizeof(text));
1510     *pcb = sizeof(text);
1511
1512     nCallbackCount++;
1513
1514     return 0;
1515   }
1516   else
1517     return 1; /* indicates callback failed */
1518 }
1519
1520 static void test_EM_StreamIn_Undo(void)
1521 {
1522   /* The purpose of this test is to determine when a EM_StreamIn should be
1523    * undoable. This is important because WM_PASTE currently uses StreamIn and
1524    * pasting should always be undoable but streaming isn't always.
1525    *
1526    * cases to test:
1527    * StreamIn plain text without SFF_SELECTION.
1528    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1529    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1530    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1531    * Feel free to add tests for other text modes or StreamIn things.
1532    */
1533
1534
1535   HWND hwndRichEdit = new_richedit(NULL);
1536   LRESULT result;
1537   EDITSTREAM es;
1538   char buffer[1024] = {0};
1539   const char randomtext[] = "Some text";
1540
1541   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1542
1543   /* StreamIn, no SFF_SELECTION */
1544   es.dwCookie = nCallbackCount;
1545   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1546   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1547   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1548   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1549   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1550   result = strcmp (buffer,"test");
1551   ok (result  == 0,
1552       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1553
1554   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1555   ok (result == FALSE,
1556       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1557
1558   /* StreamIn, SFF_SELECTION, but nothing selected */
1559   es.dwCookie = nCallbackCount;
1560   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1561   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1562   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1563   SendMessage(hwndRichEdit, EM_STREAMIN,
1564               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1565   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1566   result = strcmp (buffer,"testSome text");
1567   ok (result  == 0,
1568       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1569
1570   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1571   ok (result == TRUE,
1572      "EM_STREAMIN with SFF_SELECTION but no selection set "
1573       "should create an undo\n");
1574
1575   /* StreamIn, SFF_SELECTION, with a selection */
1576   es.dwCookie = nCallbackCount;
1577   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1578   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1579   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1580   SendMessage(hwndRichEdit, EM_STREAMIN,
1581               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1582   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1583   result = strcmp (buffer,"Sometesttext");
1584   ok (result  == 0,
1585       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1586
1587   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1588   ok (result == TRUE,
1589       "EM_STREAMIN with SFF_SELECTION and selection set "
1590       "should create an undo\n");
1591
1592 }
1593
1594 static BOOL is_em_settextex_supported(HWND hwnd)
1595 {
1596     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1597     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1598 }
1599
1600 static void test_unicode_conversions(void)
1601 {
1602     static const WCHAR tW[] = {'t',0};
1603     static const WCHAR teW[] = {'t','e',0};
1604     static const WCHAR textW[] = {'t','e','s','t',0};
1605     static const char textA[] = "test";
1606     char bufA[64];
1607     WCHAR bufW[64];
1608     HWND hwnd;
1609     int is_win9x, em_settextex_supported, ret;
1610
1611     is_win9x = GetVersion() & 0x80000000;
1612
1613 #define set_textA(hwnd, wm_set_text, txt) \
1614     do { \
1615         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1616         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1617         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1618         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1619         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1620     } while(0)
1621 #define expect_textA(hwnd, wm_get_text, txt) \
1622     do { \
1623         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1624         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1625         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1626         memset(bufA, 0xAA, sizeof(bufA)); \
1627         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1628         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1629         ret = lstrcmpA(bufA, txt); \
1630         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1631     } while(0)
1632
1633 #define set_textW(hwnd, wm_set_text, txt) \
1634     do { \
1635         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1636         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1637         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1638         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1639         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1640     } while(0)
1641 #define expect_textW(hwnd, wm_get_text, txt) \
1642     do { \
1643         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1644         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1645         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1646         memset(bufW, 0xAA, sizeof(bufW)); \
1647         if (is_win9x) \
1648         { \
1649             assert(wm_get_text == EM_GETTEXTEX); \
1650             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1651             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1652         } \
1653         else \
1654         { \
1655             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1656             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1657         } \
1658         ret = lstrcmpW(bufW, txt); \
1659         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1660     } while(0)
1661 #define expect_empty(hwnd, wm_get_text) \
1662     do { \
1663         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1664         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1665         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1666         memset(bufA, 0xAA, sizeof(bufA)); \
1667         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1668         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1669         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1670     } while(0)
1671
1672     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1673                            0, 0, 200, 60, 0, 0, 0, 0);
1674     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1675
1676     ret = IsWindowUnicode(hwnd);
1677     if (is_win9x)
1678         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1679     else
1680         ok(ret, "RichEdit20W should be unicode under NT\n");
1681
1682     /* EM_SETTEXTEX is supported starting from version 3.0 */
1683     em_settextex_supported = is_em_settextex_supported(hwnd);
1684     trace("EM_SETTEXTEX is %ssupported on this platform\n",
1685           em_settextex_supported ? "" : "NOT ");
1686
1687     expect_empty(hwnd, WM_GETTEXT);
1688     expect_empty(hwnd, EM_GETTEXTEX);
1689
1690     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1691     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1692     expect_textA(hwnd, WM_GETTEXT, "t");
1693     expect_textA(hwnd, EM_GETTEXTEX, "t");
1694     expect_textW(hwnd, EM_GETTEXTEX, tW);
1695
1696     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1697     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1698     expect_textA(hwnd, WM_GETTEXT, "te");
1699     expect_textA(hwnd, EM_GETTEXTEX, "te");
1700     expect_textW(hwnd, EM_GETTEXTEX, teW);
1701
1702     set_textA(hwnd, WM_SETTEXT, NULL);
1703     expect_empty(hwnd, WM_GETTEXT);
1704     expect_empty(hwnd, EM_GETTEXTEX);
1705
1706     if (is_win9x)
1707         set_textA(hwnd, WM_SETTEXT, textW);
1708     else
1709         set_textA(hwnd, WM_SETTEXT, textA);
1710     expect_textA(hwnd, WM_GETTEXT, textA);
1711     expect_textA(hwnd, EM_GETTEXTEX, textA);
1712     expect_textW(hwnd, EM_GETTEXTEX, textW);
1713
1714     if (em_settextex_supported)
1715     {
1716         set_textA(hwnd, EM_SETTEXTEX, textA);
1717         expect_textA(hwnd, WM_GETTEXT, textA);
1718         expect_textA(hwnd, EM_GETTEXTEX, textA);
1719         expect_textW(hwnd, EM_GETTEXTEX, textW);
1720     }
1721
1722     if (!is_win9x)
1723     {
1724         set_textW(hwnd, WM_SETTEXT, textW);
1725         expect_textW(hwnd, WM_GETTEXT, textW);
1726         expect_textA(hwnd, WM_GETTEXT, textA);
1727         expect_textW(hwnd, EM_GETTEXTEX, textW);
1728         expect_textA(hwnd, EM_GETTEXTEX, textA);
1729
1730         if (em_settextex_supported)
1731         {
1732             set_textW(hwnd, EM_SETTEXTEX, textW);
1733             expect_textW(hwnd, WM_GETTEXT, textW);
1734             expect_textA(hwnd, WM_GETTEXT, textA);
1735             expect_textW(hwnd, EM_GETTEXTEX, textW);
1736             expect_textA(hwnd, EM_GETTEXTEX, textA);
1737         }
1738     }
1739     DestroyWindow(hwnd);
1740
1741     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1742                            0, 0, 200, 60, 0, 0, 0, 0);
1743     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1744
1745     ret = IsWindowUnicode(hwnd);
1746     ok(!ret, "RichEdit20A should NOT be unicode\n");
1747
1748     set_textA(hwnd, WM_SETTEXT, textA);
1749     expect_textA(hwnd, WM_GETTEXT, textA);
1750     expect_textA(hwnd, EM_GETTEXTEX, textA);
1751     expect_textW(hwnd, EM_GETTEXTEX, textW);
1752
1753     if (em_settextex_supported)
1754     {
1755         set_textA(hwnd, EM_SETTEXTEX, textA);
1756         expect_textA(hwnd, WM_GETTEXT, textA);
1757         expect_textA(hwnd, EM_GETTEXTEX, textA);
1758         expect_textW(hwnd, EM_GETTEXTEX, textW);
1759     }
1760
1761     if (!is_win9x)
1762     {
1763         set_textW(hwnd, WM_SETTEXT, textW);
1764         expect_textW(hwnd, WM_GETTEXT, textW);
1765         expect_textA(hwnd, WM_GETTEXT, textA);
1766         expect_textW(hwnd, EM_GETTEXTEX, textW);
1767         expect_textA(hwnd, EM_GETTEXTEX, textA);
1768
1769         if (em_settextex_supported)
1770         {
1771             set_textW(hwnd, EM_SETTEXTEX, textW);
1772             expect_textW(hwnd, WM_GETTEXT, textW);
1773             expect_textA(hwnd, WM_GETTEXT, textA);
1774             expect_textW(hwnd, EM_GETTEXTEX, textW);
1775             expect_textA(hwnd, EM_GETTEXTEX, textA);
1776         }
1777     }
1778     DestroyWindow(hwnd);
1779 }
1780
1781 START_TEST( editor )
1782 {
1783   MSG msg;
1784   time_t end;
1785
1786   /* Must explicitly LoadLibrary(). The test has no references to functions in
1787    * RICHED20.DLL, so the linker doesn't actually link to it. */
1788   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1789   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1790
1791   test_EM_FINDTEXT();
1792   test_EM_GETLINE();
1793   test_EM_SCROLLCARET();
1794   test_EM_SCROLL();
1795   test_EM_SETTEXTMODE();
1796   test_TM_PLAINTEXT();
1797   test_EM_SETOPTIONS();
1798   test_WM_GETTEXT();
1799   test_EM_AUTOURLDETECT();
1800   test_EM_SETUNDOLIMIT();
1801   test_ES_PASSWORD();
1802   test_EM_SETTEXTEX();
1803   test_EM_LIMITTEXT();
1804   test_EM_EXLIMITTEXT();
1805   test_EM_GETLIMITTEXT();
1806   test_WM_SETFONT();
1807   test_EM_GETMODIFY();
1808   test_EM_EXSETSEL();
1809   test_WM_PASTE();
1810   test_EM_StreamIn_Undo();
1811   test_unicode_conversions();
1812
1813   /* Set the environment variable WINETEST_RICHED20 to keep windows
1814    * responsive and open for 30 seconds. This is useful for debugging.
1815    *
1816    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1817    * 50ms before retrying the queue. */
1818   end = time(NULL) + 30;
1819   if (getenv( "WINETEST_RICHED20" )) {
1820     while (time(NULL) < end) {
1821       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1822         TranslateMessage(&msg);
1823         DispatchMessage(&msg);
1824       } else {
1825         Sleep(50);
1826       }
1827     }
1828   }
1829
1830   OleFlushClipboard();
1831   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1832 }