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