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