riched20/tests: Win64 printf format warning fixes.
[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 }
574
575 /* FIXME: need to test unimplemented options and robustly test wparam */
576 static void test_EM_SETOPTIONS(void)
577 {
578     HWND hwndRichEdit = new_richedit(NULL);
579     static const char text[] = "Hello. My name is RichEdit!";
580     char buffer[1024] = {0};
581
582     /* NEGATIVE TESTING - NO OPTIONS SET */
583     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
584     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
585
586     /* testing no readonly by sending 'a' to the control*/
587     SetFocus(hwndRichEdit);
588     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
589     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
590     ok(buffer[0]=='a', 
591        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
592     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593
594     /* READONLY - sending 'a' to the control */
595     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
596     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
597     SetFocus(hwndRichEdit);
598     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
599     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600     ok(buffer[0]==text[0], 
601        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
602
603     DestroyWindow(hwndRichEdit);
604 }
605
606 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
607 {
608   CHARFORMAT2W text_format;
609   int link_present = 0;
610   text_format.cbSize = sizeof(text_format);
611   SendMessage(hwnd, EM_SETSEL, 0, 0);
612   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
613   link_present = text_format.dwEffects & CFE_LINK;
614   if (is_url) 
615   { /* control text is url; should get CFE_LINK */
616         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
617   }
618   else 
619   {
620     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
621   }
622 }
623
624 static HWND new_static_wnd(HWND parent) {
625   return new_window("Static", 0, parent);
626 }
627
628 static void test_EM_AUTOURLDETECT(void)
629 {
630   struct urls_s {
631     const char *text;
632     int is_url;
633   } urls[12] = {
634     {"winehq.org", 0},
635     {"http://www.winehq.org", 1},
636     {"http//winehq.org", 0},
637     {"ww.winehq.org", 0},
638     {"www.winehq.org", 1},
639     {"ftp://192.168.1.1", 1},
640     {"ftp//192.168.1.1", 0},
641     {"mailto:your@email.com", 1},    
642     {"prospero:prosperoserver", 1},
643     {"telnet:test", 1},
644     {"news:newserver", 1},
645     {"wais:waisserver", 1}  
646   };
647
648   int i;
649   int urlRet=-1;
650   HWND hwndRichEdit, parent;
651
652   parent = new_static_wnd(NULL);
653   hwndRichEdit = new_richedit(parent);
654   /* Try and pass EM_AUTOURLDETECT some test wParam values */
655   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
656   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
657   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
658   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
659   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
660   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
661   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
662   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
663   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
664   /* for each url, check the text to see if CFE_LINK effect is present */
665   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
666     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
667     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
668     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
669     check_CFE_LINK_rcvd(hwndRichEdit, 0);
670     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
671     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
672     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
673     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
674   }
675   DestroyWindow(hwndRichEdit);
676   DestroyWindow(parent);
677 }
678
679 static void test_EM_SCROLL(void)
680 {
681   int i, j;
682   int r; /* return value */
683   int expr; /* expected return value */
684   HWND hwndRichEdit = new_richedit(NULL);
685   int y_before, y_after; /* units of lines of text */
686
687   /* test a richedit box containing a single line of text */
688   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
689   expr = 0x00010000;
690   for (i = 0; i < 4; i++) {
691     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
692
693     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
694     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
695     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
696        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
697     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
698        "(i == %d)\n", y_after, i);
699   }
700
701   /*
702    * test a richedit box that will scroll. There are two general
703    * cases: the case without any long lines and the case with a long
704    * line.
705    */
706   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
707     if (i == 0)
708       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
709     else
710       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
711                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
712                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
713                   "LONG LINE \nb\nc\nd\ne");
714     for (j = 0; j < 12; j++) /* reset scrol position to top */
715       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
716
717     /* get first visible line */
718     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
719     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
720
721     /* get new current first visible line */
722     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
723
724     ok(((r & 0xffffff00) == 0x00010000) &&
725        ((r & 0x000000ff) != 0x00000000),
726        "EM_SCROLL page down didn't scroll by a small positive number of "
727        "lines (r == 0x%08x)\n", r);
728     ok(y_after > y_before, "EM_SCROLL page down not functioning "
729        "(line %d scrolled to line %d\n", y_before, y_after);
730
731     y_before = y_after;
732     
733     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
734     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
735     ok(((r & 0xffffff00) == 0x0001ff00),
736        "EM_SCROLL page up didn't scroll by a small negative number of lines "
737        "(r == 0x%08x)\n", r);
738     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
739        "%d scrolled to line %d\n", y_before, y_after);
740     
741     y_before = y_after;
742
743     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
744
745     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
746
747     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
748        "(r == 0x%08x)\n", r);
749     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
750        "1 line (%d scrolled to %d)\n", y_before, y_after);
751
752     y_before = y_after;
753
754     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
755
756     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
757
758     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
759        "(r == 0x%08x)\n", r);
760     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
761        "line (%d scrolled to %d)\n", y_before, y_after);
762
763     y_before = y_after;
764
765     r = SendMessage(hwndRichEdit, EM_SCROLL,
766                     SB_LINEUP, 0); /* lineup beyond top */
767
768     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
769
770     ok(r == 0x00010000,
771        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
772     ok(y_before == y_after,
773        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
774
775     y_before = y_after;
776
777     r = SendMessage(hwndRichEdit, EM_SCROLL,
778                     SB_PAGEUP, 0);/*page up beyond top */
779
780     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
781
782     ok(r == 0x00010000,
783        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
784     ok(y_before == y_after,
785        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
786
787     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
788       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
789     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
790     r = SendMessage(hwndRichEdit, EM_SCROLL,
791                     SB_PAGEDOWN, 0); /* page down beyond bot */
792     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
793
794     ok(r == 0x00010000,
795        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
796     ok(y_before == y_after,
797        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
798        y_before, y_after);
799
800     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
801     SendMessage(hwndRichEdit, EM_SCROLL,
802                 SB_LINEDOWN, 0); /* line down beyond bot */
803     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
804     
805     ok(r == 0x00010000,
806        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
807     ok(y_before == y_after,
808        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
809        y_before, y_after);
810   }
811   DestroyWindow(hwndRichEdit);
812 }
813
814 static void test_EM_SETUNDOLIMIT(void)
815 {
816   /* cases we test for:
817    * default behaviour - limiting at 100 undo's 
818    * undo disabled - setting a limit of 0
819    * undo limited -  undo limit set to some to some number, like 2
820    * bad input - sending a negative number should default to 100 undo's */
821  
822   HWND hwndRichEdit = new_richedit(NULL);
823   CHARRANGE cr;
824   int i;
825   int result;
826   
827   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
828   cr.cpMin = 0;
829   cr.cpMax = 1;
830   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
831     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
832       also, multiple pastes don't combine like WM_CHAR would */
833   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
834
835   /* first case - check the default */
836   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
837   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
838     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
839   for (i=0; i<100; i++) /* Undo 100 of them */
840     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
841   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
842      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
843
844   /* second case - cannot undo */
845   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
846   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
847   SendMessage(hwndRichEdit,
848               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
849   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
850      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
851
852   /* third case - set it to an arbitrary number */
853   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
854   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
855   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
856   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
857   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
858   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
859   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
860      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
861   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
862   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
863      "EM_SETUNDOLIMIT didn't allow a second 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 allowed a third undo with UNDOLIMIT set to 2\n");
867   
868   /* fourth case - setting negative numbers should default to 100 undos */
869   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
870   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
871   ok (result == 100, 
872       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
873       
874   DestroyWindow(hwndRichEdit);
875 }
876
877 static void test_ES_PASSWORD()
878 {
879   /* This isn't hugely testable, so we're just going to run it through it's paces. */
880         
881   HWND hwndRichEdit = new_richedit(NULL);
882   WCHAR result;
883         
884   /* First, check the default of a regular control */
885   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
886   ok (result == 0,
887         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
888   
889   /* Now, set it to something normal */
890   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
891   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
892   ok (result == 120,
893         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
894         
895   /* Now, set it to something odd */    
896   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
897   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
898   ok (result == 1234,
899         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
900 }
901
902 static void test_EM_SETTEXTEX()
903 {
904   HWND hwndRichEdit = new_richedit(NULL);
905   SETTEXTEX setText;
906   GETTEXTEX getText;
907   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
908                        'S', 'o', 'm', 'e', 
909                        'T', 'e', 'x', 't', 0}; 
910 #define MAX_BUF_LEN 1024
911   WCHAR buf[MAX_BUF_LEN];
912   int result;
913   CHARRANGE cr;
914
915   setText.codepage = 1200;  /* no constant for unicode */
916   getText.codepage = 1200;  /* no constant for unicode */
917   getText.cb = MAX_BUF_LEN;
918   getText.flags = GT_DEFAULT;
919
920   setText.flags = 0;
921   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
922   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
923   ok(lstrcmpW(buf, TestItem1) == 0,
924       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
925
926   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
927                        (WPARAM)&setText, (LPARAM) NULL);
928   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
929   
930   ok (result == 1, 
931       "EM_SETTEXTEX returned %d, instead of 1\n",result);
932   ok(lstrlenW(buf) == 0,
933       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
934   
935   /* put some text back */
936   setText.flags = 0;
937   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
938   /* select some text */
939   cr.cpMax = 1;
940   cr.cpMin = 3;
941   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
942   /* replace current selection */
943   setText.flags = ST_SELECTION;
944   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
945                        (WPARAM)&setText, (LPARAM) NULL);
946   ok(result == 0,
947       "EM_SETTEXTEX with NULL lParam to replace selection"
948       " with no text should return 0. Got %i\n",
949       result);
950   
951   /* put some text back */
952   setText.flags = 0;
953   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
954   /* select some text */
955   cr.cpMax = 1;
956   cr.cpMin = 3;
957   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
958   /* replace current selection */
959   setText.flags = ST_SELECTION;
960   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
961                        (WPARAM)&setText, (LPARAM) TestItem1);
962   /* get text */
963   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
964   ok(result == lstrlenW(TestItem1),
965       "EM_SETTEXTEX with NULL lParam to replace selection"
966       " with no text should return 0. Got %i\n",
967       result);
968   ok(lstrlenW(buf) == 22,
969       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
970       lstrlenW(buf) );
971
972   DestroyWindow(hwndRichEdit);
973 }
974
975
976 static void test_EM_EXLIMITTEXT(void)
977 {
978   int i, selBegin, selEnd, len1, len2;
979   char text[1024 + 1];
980   int textlimit = 0; /* multiple of 100 */
981   HWND hwndRichEdit = new_richedit(NULL);
982   
983   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
984   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
985   
986   textlimit = 256000;
987   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
988   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
989   /* set higher */
990   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
991   
992   textlimit = 1000;
993   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
994   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
995   /* set lower */
996   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
997  
998   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
999   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1000   /* default for WParam = 0 */
1001   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1002  
1003   textlimit = sizeof(text)-1;
1004   memset(text, 'W', textlimit);
1005   text[sizeof(text)-1] = 0;
1006   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1007   /* maxed out text */
1008   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1009   
1010   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1011   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1012   len1 = selEnd - selBegin;
1013   
1014   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1015   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1016   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1017   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1018   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1019   len2 = selEnd - selBegin;
1020   
1021   ok(len1 != len2,
1022     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1023     len1,len2,i);
1024   
1025   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1026   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1027   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1028   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1029   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1030   len1 = selEnd - selBegin;
1031   
1032   ok(len1 != len2,
1033     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1034     len1,len2,i);
1035   
1036   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1037   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1038   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1039   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1040   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1041   len2 = selEnd - selBegin;
1042   
1043   ok(len1 == len2, 
1044     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1045     len1,len2,i);
1046
1047   DestroyWindow(hwndRichEdit);
1048 }
1049
1050 static void test_EM_GETLIMITTEXT(void)
1051 {
1052   int i;
1053   HWND hwndRichEdit = new_richedit(NULL);
1054
1055   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1056   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1057
1058   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1059   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1060   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1061
1062   DestroyWindow(hwndRichEdit);
1063 }
1064
1065 static void test_WM_SETFONT()
1066 {
1067   /* There is no invalid input or error conditions for this function.
1068    * NULL wParam and lParam just fall back to their default values 
1069    * It should be noted that even if you use a gibberish name for your fonts
1070    * here, it will still work because the name is stored. They will display as
1071    * System, but will report their name to be whatever they were created as */
1072   
1073   HWND hwndRichEdit = new_richedit(NULL);
1074   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1075     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1076     FF_DONTCARE, "Marlett");
1077   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1078     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1079     FF_DONTCARE, "MS Sans Serif");
1080   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1081     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1082     FF_DONTCARE, "Courier");
1083   LOGFONTA sentLogFont;
1084   CHARFORMAT2A returnedCF2A;
1085   
1086   returnedCF2A.cbSize = sizeof(returnedCF2A);
1087   
1088   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1089   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1090   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1091
1092   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1093   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1094     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1095     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1096
1097   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1098   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1099   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1100   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1101     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1102     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1103     
1104   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1105   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1106   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1107   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1108     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1109     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1110    
1111   /* This last test is special since we send in NULL. We clear the variables
1112    * and just compare to "System" instead of the sent in font name. */
1113   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1114   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1115   returnedCF2A.cbSize = sizeof(returnedCF2A);
1116   
1117   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1118   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1119   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1120   ok (!strcmp("System",returnedCF2A.szFaceName),
1121     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1122   
1123   DestroyWindow(hwndRichEdit);
1124 }
1125
1126
1127 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1128                                          LPBYTE pbBuff,
1129                                          LONG cb,
1130                                          LONG *pcb)
1131 {
1132   const char** str = (const char**)dwCookie;
1133   int size = strlen(*str);
1134   if(size > 3)  /* let's make it peice-meal for fun */
1135     size = 3;
1136   *pcb = cb;
1137   if (*pcb > size) {
1138     *pcb = size;
1139   }
1140   if (*pcb > 0) {
1141     memcpy(pbBuff, *str, *pcb);
1142     *str += *pcb;
1143   }
1144   return 0;
1145 }
1146
1147 static void test_EM_GETMODIFY(void)
1148 {
1149   HWND hwndRichEdit = new_richedit(NULL);
1150   LRESULT result;
1151   SETTEXTEX setText;
1152   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1153                        'S', 'o', 'm', 'e', 
1154                        'T', 'e', 'x', 't', 0}; 
1155   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1156                        'S', 'o', 'm', 'e', 
1157                        'O', 't', 'h', 'e', 'r',
1158                        'T', 'e', 'x', 't', 0}; 
1159   const char* streamText = "hello world";
1160   CHARFORMAT2 cf2;
1161   PARAFORMAT2 pf2;
1162   EDITSTREAM es;
1163   
1164   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1165     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1166     FF_DONTCARE, "Courier");
1167   
1168   setText.codepage = 1200;  /* no constant for unicode */
1169   setText.flags = ST_KEEPUNDO;
1170   
1171
1172   /* modify flag shouldn't be set when richedit is first created */
1173   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1174   ok (result == 0, 
1175       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1176   
1177   /* setting modify flag should actually set it */
1178   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1179   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1180   ok (result != 0, 
1181       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1182   
1183   /* clearing modify flag should actually clear it */
1184   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1185   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1186   ok (result == 0, 
1187       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1188  
1189   /* setting font doesn't change modify flag */
1190   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1191   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1192   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1193   todo_wine {
1194   ok (result == 0,
1195       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1196   }
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   todo_wine {
1209   ok (result != 0,
1210       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1211   }
1212   
1213   /* set text with no flag to keep undo stack should not set modify flag */
1214   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1215   setText.flags = 0;
1216   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1217   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1218   todo_wine {
1219   ok (result == 0,
1220       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1221   }
1222   
1223   /* WM_SETTEXT doesn't modify */
1224   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1225   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1226   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1227   todo_wine {
1228   ok (result == 0,
1229       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1230   }
1231   
1232   /* clear the text */
1233   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1234   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1235   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1236   ok (result == 0,
1237       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1238   
1239   /* replace text */
1240   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1241   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1242   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1243   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1244   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1245   ok (result != 0,
1246       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1247   
1248   /* copy/paste text 1 */
1249   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1250   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1251   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1252   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1253   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1254   ok (result != 0,
1255       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1256   
1257   /* copy/paste text 2 */
1258   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1259   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1260   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1261   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1262   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1263   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1264   ok (result != 0,
1265       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1266   
1267   /* press char */
1268   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1269   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1270   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1271   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1272   ok (result != 0,
1273       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1274   
1275   /* set char format */
1276   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1277   cf2.cbSize = sizeof(CHARFORMAT2);
1278   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1279              (LPARAM) &cf2);
1280   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1281   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1282   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1283   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1284   ok (result != 0,
1285       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1286   
1287   /* set para format */
1288   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1289   pf2.cbSize = sizeof(PARAFORMAT2);
1290   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1291              (LPARAM) &pf2);
1292   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1293   pf2.wAlignment = PFA_RIGHT;
1294   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1295   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1296   todo_wine {
1297   ok (result == 0,
1298       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1299   }
1300
1301   /* EM_STREAM */
1302   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1303   es.dwCookie = (DWORD_PTR)&streamText;
1304   es.dwError = 0;
1305   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1306   SendMessage(hwndRichEdit, EM_STREAMIN, 
1307               (WPARAM)(SF_TEXT), (LPARAM)&es);
1308   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1309   todo_wine {
1310   ok (result != 0,
1311       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1312   }
1313
1314   
1315   DestroyWindow(hwndRichEdit);
1316 }
1317
1318 static void test_EM_EXSETSEL(void)
1319 {
1320     HWND hwndRichEdit = new_richedit(NULL);
1321     long result;
1322     CHARRANGE cr;
1323     int start,end;
1324
1325     /* sending some text to the window */
1326     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1327     /*                                                 01234567890123456*/
1328     /*                                                          10      */
1329
1330     /* EM_EXSETSEL returns (cr.cpMax < strlen+1 ? cr.cpMax:strlen+1 if cpMin is positive */
1331     cr.cpMin = 0;
1332     cr.cpMax = 100;
1333     result = SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1334     ok(result == 18, "EM_EXSETSEL: expected: 18 actual: %ld\n", result);
1335     SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1336
1337     /* FIXME: EM_GETSEL needs to return proper ending value */
1338     todo_wine
1339     {
1340         ok(start == 0 && end == 18, "EM_EXSETSEL: expected (0,18) actual:(%d,%d)\n", start,end);
1341     }
1342
1343     cr.cpMin = 5;
1344     cr.cpMax = 10;
1345     result = SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1346     ok(result == 10, "EM_EXSETSEL: expected: 10 actual: %ld\n", result);
1347     SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1348     ok(start == 5 && end == 10, "EM_EXSETSEL: expected (5,10) actual:(%d,%d)\n", start,end);
1349
1350     cr.cpMin = 15;
1351     cr.cpMax = 17;
1352     result = SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1353     ok(result == 17, "EM_EXSETSEL: expected: 17 actual: %ld\n", result);
1354     SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1355     ok(start == 15 && end == 17, "EM_EXSETSEL: expected (15,17) actual:(%d,%d)\n", start,end);
1356
1357     /* bug 4462 - the following values get used in FileZilla */
1358     /* when min < 0, selection is deselected and caret is moved to end of last selection */
1359     cr.cpMin = -3;
1360     cr.cpMax = 0;
1361     result = SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1362     ok(result == 17, "EM_EXSETSEL: expected: 17 actual: %ld\n", result);
1363     SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1364     ok(start == 17 && end == 17, "EM_EXSETSEL: expected (17,17) actual:(%d,%d)\n", start,end);
1365
1366     DestroyWindow(hwndRichEdit);
1367 }
1368
1369 START_TEST( editor )
1370 {
1371   MSG msg;
1372   time_t end;
1373
1374   /* Must explicitly LoadLibrary(). The test has no references to functions in
1375    * RICHED20.DLL, so the linker doesn't actually link to it. */
1376   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1377   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1378   test_EM_FINDTEXT();
1379   test_EM_GETLINE();
1380   test_EM_SCROLLCARET();
1381   test_EM_SCROLL();
1382   test_EM_SETTEXTMODE();
1383   test_TM_PLAINTEXT();
1384   test_EM_SETOPTIONS();
1385   test_WM_GETTEXT();
1386   test_EM_AUTOURLDETECT();
1387   test_EM_SETUNDOLIMIT();
1388   test_ES_PASSWORD();
1389   test_EM_SETTEXTEX();
1390   test_EM_EXLIMITTEXT();
1391   test_EM_GETLIMITTEXT();
1392   test_WM_SETFONT();
1393   test_EM_GETMODIFY();
1394   test_EM_EXSETSEL();
1395
1396   /* Set the environment variable WINETEST_RICHED20 to keep windows
1397    * responsive and open for 30 seconds. This is useful for debugging.
1398    *
1399    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1400    * 50ms before retrying the queue. */
1401   end = time(NULL) + 30;
1402   if (getenv( "WINETEST_RICHED20" )) {
1403     while (time(NULL) < end) {
1404       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1405         TranslateMessage(&msg);
1406         DispatchMessage(&msg);
1407       } else {
1408         Sleep(50);
1409       }
1410     }
1411   }
1412
1413   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1414 }