quartz: Remove superfluous pointer casts.
[wine] / dlls / riched32 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control 1.0
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 * Copyright 2007 Alex VillacĂ­s Lasso
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
35
36 static HMODULE hmoduleRichEdit;
37
38 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
39   HWND hwnd;
40   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
41                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
42                       hmoduleRichEdit, NULL);
43   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
44   return hwnd;
45 }
46
47 static HWND new_richedit(HWND parent) {
48   return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
49 }
50
51 static void test_WM_SETTEXT()
52 {
53   HWND hwndRichEdit = new_richedit(NULL);
54   const char * TestItem1 = "TestSomeText";
55   const char * TestItem2 = "TestSomeText\r";
56   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
57   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
58   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
59   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
60   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
61   const char * TestItem8 = "TestSomeText\r\n";
62   const char * TestItem9 = "TestSomeText\r\nSomeMoreText\r\n";
63   const char * TestItem10 = "TestSomeText\r\n\r\nTestSomeText";
64   const char * TestItem11 = "TestSomeText TestSomeText";
65   const char * TestItem12 = "TestSomeText \r\nTestSomeText";
66   const char * TestItem13 = "TestSomeText\r\n \r\nTestSomeText";
67   const char * TestItem14 = "TestSomeText\n";
68   const char * TestItem15 = "TestSomeText\r\r\r";
69   const char * TestItem16 = "TestSomeText\r\r\rSomeMoreText";
70   char buf[1024] = {0};
71   LRESULT result;
72
73   /* This test attempts to show that WM_SETTEXT on a riched32 control does not
74    * attempt to modify the text that is pasted into the control, and should
75    * return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
76    *
77    * For riched32, the rules for breaking lines seem to be the following:
78    * - \r\n is one line break. This is the normal case.
79    * - \r{0,2}\n is one line break. In particular, \n by itself is a line break.
80    * - \r{0,N-1}\r\r\n is N line breaks.
81    * - \n{1,N} are that many line breaks.
82    * - \r with text or other characters (except \n) past it, is a line break. That
83    *   is, a run of \r{N} without a terminating \n is considered N line breaks
84    * - \r at the end of the text is NOT a line break. This differs from riched20,
85    *   where \r at the end of the text is a proper line break.
86    */
87
88 #define TEST_SETTEXT(a, b, nlines) \
89   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
90   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
91   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
92   ok (result == lstrlen(buf), \
93         "WM_GETTEXT returned %ld instead of expected %u\n", \
94         result, lstrlen(buf)); \
95   result = strcmp(b, buf); \
96   ok(result == 0, \
97         "WM_SETTEXT round trip: strcmp = %ld\n", result); \
98   result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); \
99   ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines);
100
101   TEST_SETTEXT(TestItem1, TestItem1, 1)
102   TEST_SETTEXT(TestItem2, TestItem2, 1)
103   TEST_SETTEXT(TestItem3, TestItem3, 2)
104   TEST_SETTEXT(TestItem4, TestItem4, 3)
105   TEST_SETTEXT(TestItem5, TestItem5, 2)
106   TEST_SETTEXT(TestItem6, TestItem6, 3)
107   TEST_SETTEXT(TestItem7, TestItem7, 4)
108   TEST_SETTEXT(TestItem8, TestItem8, 2)
109   TEST_SETTEXT(TestItem9, TestItem9, 3)
110   TEST_SETTEXT(TestItem10, TestItem10, 3)
111   TEST_SETTEXT(TestItem11, TestItem11, 1)
112   TEST_SETTEXT(TestItem12, TestItem12, 2)
113   TEST_SETTEXT(TestItem13, TestItem13, 3)
114   TEST_SETTEXT(TestItem14, TestItem14, 2)
115   TEST_SETTEXT(TestItem15, TestItem15, 3)
116   TEST_SETTEXT(TestItem16, TestItem16, 4)
117
118 #undef TEST_SETTEXT
119   DestroyWindow(hwndRichEdit);
120 }
121
122 static void test_WM_GETTEXTLENGTH(void)
123 {
124     HWND hwndRichEdit = new_richedit(NULL);
125     static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
126     static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
127     int result;
128
129     /* Test for WM_GETTEXTLENGTH */
130     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
131     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
132     ok(result == lstrlen(text3),
133         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
134         result, lstrlen(text3));
135
136     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
137     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
138     ok(result == lstrlen(text4),
139         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
140         result, lstrlen(text4));
141
142     DestroyWindow(hwndRichEdit);
143 }
144
145 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
146                                          LPBYTE pbBuff,
147                                          LONG cb,
148                                          LONG *pcb)
149 {
150   const char** str = (const char**)dwCookie;
151   int size = strlen(*str);
152   *pcb = cb;
153   if (*pcb > size) {
154     *pcb = size;
155   }
156   if (*pcb > 0) {
157     memcpy(pbBuff, *str, *pcb);
158     *str += *pcb;
159   }
160   return 0;
161 }
162
163
164 static void test_EM_STREAMIN(void)
165 {
166   HWND hwndRichEdit = new_richedit(NULL);
167   LRESULT result;
168   EDITSTREAM es;
169   char buffer[1024] = {0};
170
171   const char * streamText0 = "{\\rtf1 TestSomeText}";
172   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
173   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
174
175   const char * streamText1 =
176   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
177   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
178   "}\r\n";
179
180   /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
181   const char * streamText2 =
182     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
183     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
184     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
185     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
186     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
187     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
188     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
189
190   const char * streamText3 = "RichEdit1";
191
192   /* Minimal test without \par at the end */
193   es.dwCookie = (DWORD_PTR)&streamText0;
194   es.dwError = 0;
195   es.pfnCallback = test_EM_STREAMIN_esCallback;
196   SendMessage(hwndRichEdit, EM_STREAMIN,
197               (WPARAM)(SF_RTF), (LPARAM)&es);
198
199   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
200   ok (result  == 12,
201       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
202   result = strcmp (buffer,"TestSomeText");
203   ok (result  == 0,
204       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
205   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
206
207   /* Native richedit 2.0 ignores last \par */
208   es.dwCookie = (DWORD_PTR)&streamText0a;
209   es.dwError = 0;
210   es.pfnCallback = test_EM_STREAMIN_esCallback;
211   SendMessage(hwndRichEdit, EM_STREAMIN,
212               (WPARAM)(SF_RTF), (LPARAM)&es);
213
214   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
215   ok (result  == 12,
216       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
217   result = strcmp (buffer,"TestSomeText");
218   ok (result  == 0,
219       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
220   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
221
222   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
223   es.dwCookie = (DWORD_PTR)&streamText0b;
224   es.dwError = 0;
225   es.pfnCallback = test_EM_STREAMIN_esCallback;
226   SendMessage(hwndRichEdit, EM_STREAMIN,
227               (WPARAM)(SF_RTF), (LPARAM)&es);
228
229   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
230   ok (result  == 14,
231       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
232   result = strcmp (buffer,"TestSomeText\r\n");
233   ok (result  == 0,
234       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
235   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
236
237   es.dwCookie = (DWORD_PTR)&streamText1;
238   es.dwError = 0;
239   es.pfnCallback = test_EM_STREAMIN_esCallback;
240   SendMessage(hwndRichEdit, EM_STREAMIN,
241               (WPARAM)(SF_RTF), (LPARAM)&es);
242
243   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
244   ok (result  == 12,
245       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
246   result = strcmp (buffer,"TestSomeText");
247   ok (result  == 0,
248       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
249   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
250
251
252   es.dwCookie = (DWORD_PTR)&streamText2;
253   es.dwError = 0;
254   SendMessage(hwndRichEdit, EM_STREAMIN,
255               (WPARAM)(SF_RTF), (LPARAM)&es);
256
257   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
258   todo_wine {
259   ok (result  == 9,
260       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
261   }
262   result = strcmp (buffer,"RichEdit1");
263   todo_wine {
264   ok (result  == 0,
265       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
266   }
267   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
268
269   es.dwCookie = (DWORD_PTR)&streamText3;
270   es.dwError = 0;
271   SendMessage(hwndRichEdit, EM_STREAMIN,
272               (WPARAM)(SF_RTF), (LPARAM)&es);
273
274   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
275   ok (result  == 0,
276       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
277   ok (strlen(buffer)  == 0,
278       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
279   ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
280
281   DestroyWindow(hwndRichEdit);
282 }
283
284 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
285                                          LPBYTE pbBuff,
286                                          LONG cb,
287                                          LONG *pcb)
288 {
289   char** str = (char**)dwCookie;
290   *pcb = cb;
291   if (*pcb > 0) {
292     memcpy(*str, pbBuff, *pcb);
293     *str += *pcb;
294   }
295   return 0;
296 }
297
298 static void test_EM_STREAMOUT(void)
299 {
300   HWND hwndRichEdit = new_richedit(NULL);
301   int r;
302   EDITSTREAM es;
303   char buf[1024] = {0};
304   char * p;
305
306   const char * TestItem1 = "TestSomeText";
307   const char * TestItem2 = "TestSomeText\r";
308   const char * TestItem3 = "TestSomeText\r\n";
309
310   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
311   p = buf;
312   es.dwCookie = (DWORD_PTR)&p;
313   es.dwError = 0;
314   es.pfnCallback = test_WM_SETTEXT_esCallback;
315   memset(buf, 0, sizeof(buf));
316   SendMessage(hwndRichEdit, EM_STREAMOUT,
317               (WPARAM)(SF_TEXT), (LPARAM)&es);
318   r = strlen(buf);
319   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
320   ok(strcmp(buf, TestItem1) == 0,
321         "streamed text different, got %s\n", buf);
322
323   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
324   p = buf;
325   es.dwCookie = (DWORD_PTR)&p;
326   es.dwError = 0;
327   es.pfnCallback = test_WM_SETTEXT_esCallback;
328   memset(buf, 0, sizeof(buf));
329   SendMessage(hwndRichEdit, EM_STREAMOUT,
330               (WPARAM)(SF_TEXT), (LPARAM)&es);
331   r = strlen(buf);
332
333   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
334   ok(strcmp(buf, TestItem2) == 0,
335         "streamed text different, got %s\n", buf);
336
337   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
338   p = buf;
339   es.dwCookie = (DWORD_PTR)&p;
340   es.dwError = 0;
341   es.pfnCallback = test_WM_SETTEXT_esCallback;
342   memset(buf, 0, sizeof(buf));
343   SendMessage(hwndRichEdit, EM_STREAMOUT,
344               (WPARAM)(SF_TEXT), (LPARAM)&es);
345   r = strlen(buf);
346   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
347   ok(strcmp(buf, TestItem3) == 0,
348         "streamed text different, got %s\n", buf);
349
350   DestroyWindow(hwndRichEdit);
351 }
352
353 static const struct getline_s {
354   int line;
355   size_t buffer_len;
356   const char *text;
357 } gl[] = {
358   {0, 10, "foo bar\r\n"},
359   {1, 10, "\r"},
360   {2, 10, "\r\r\n"},
361   {3, 10, "bar\n"},
362   {4, 10, "\r\n"},
363
364   /* Buffer smaller than line length */
365   {0, 2, "foo bar\r"},
366   {0, 1, "foo bar\r"},
367   {0, 0, "foo bar\r"}
368 };
369
370 static void test_EM_GETLINE(void)
371 {
372   int i;
373   HWND hwndRichEdit = new_richedit(NULL);
374   static const int nBuf = 1024;
375   char dest[1024], origdest[1024];
376   const char text[] = "foo bar\r\n"
377       "\r"
378       "\r\r\n"
379       "bar\n";
380
381   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
382
383   memset(origdest, 0xBB, nBuf);
384   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
385   {
386     int nCopied;
387     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
388     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
389     memset(dest, 0xBB, nBuf);
390     *(WORD *) dest = gl[i].buffer_len;
391
392     /* EM_GETLINE appends a "\r\0" to the end of the line
393      * nCopied counts up to and including the '\r' */
394     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
395     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
396        expected_nCopied);
397     /* two special cases since a parameter is passed via dest */
398     if (gl[i].buffer_len == 0)
399       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
400          "buffer_len=0\n");
401     else if (gl[i].buffer_len == 1)
402       ok(dest[0] == gl[i].text[0] && !dest[1] &&
403          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
404     else
405     {
406       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
407          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
408       ok(!strncmp(dest + expected_bytes_written, origdest
409                   + expected_bytes_written, nBuf - expected_bytes_written),
410          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
411     }
412   }
413
414   DestroyWindow(hwndRichEdit);
415 }
416
417 static void test_EM_LINELENGTH(void)
418 {
419   HWND hwndRichEdit = new_richedit(NULL);
420   const char * text =
421         "richedit1\r"
422         "richedit1\n"
423         "richedit1\r\n"
424         "short\r"
425         "richedit1\r"
426         "\r"
427         "\r"
428         "\r\r\n";
429   int offset_test[16][2] = {
430         {0, 9},  /* Line 1: |richedit1\r */
431         {5, 9},  /* Line 1: riche|dit1\r */
432         {10, 9}, /* Line 2: |richedit1\n */
433         {15, 9}, /* Line 2: riche|dit1\n */
434         {20, 9}, /* Line 3: |richedit1\r\n */
435         {25, 9}, /* Line 3: riche|dit1\r\n */
436         {30, 9}, /* Line 3: richedit1\r|\n */
437         {31, 5}, /* Line 4: |short\r */
438         {42, 9}, /* Line 5: riche|dit1\r */
439         {46, 9}, /* Line 5: richedit1|\r */
440         {47, 0}, /* Line 6: |\r */
441         {48, 0}, /* Line 7: |\r */
442         {49, 0}, /* Line 8: |\r\r\n */
443         {50, 0}, /* Line 8: \r|\r\n */
444         {51, 0}, /* Line 8: \r\r|\n */
445         {52, 0}, /* Line 9: \r\r\n| */
446   };
447   int i;
448   LRESULT result;
449
450   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
451
452   result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
453   ok(result == 9, "Incorrect line count of %ld\n", result);
454
455   for (i = 0; i < sizeof(offset_test)/sizeof(offset_test[0]); i++) {
456     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
457     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
458        offset_test[i][0], result, offset_test[i][1]);
459   }
460
461   DestroyWindow(hwndRichEdit);
462 }
463
464 static void test_EM_GETTEXTRANGE(void)
465 {
466     HWND hwndRichEdit = new_richedit(NULL);
467     const char * text1 = "foo bar\r\nfoo bar";
468     const char * text3 = "foo bar\rfoo bar";
469     const char * expect1 = "bar\r\nfoo";
470     const char * expect2 = "\nfoo";
471     const char * expect3 = "bar\rfoo";
472     char buffer[1024] = {0};
473     LRESULT result;
474     TEXTRANGEA textRange;
475
476     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
477
478     textRange.lpstrText = buffer;
479     textRange.chrg.cpMin = 4;
480     textRange.chrg.cpMax = 12;
481     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
482     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
483     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
484
485     textRange.lpstrText = buffer;
486     textRange.chrg.cpMin = 8;
487     textRange.chrg.cpMax = 12;
488     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
489     ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
490     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
491
492     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
493
494     textRange.lpstrText = buffer;
495     textRange.chrg.cpMin = 4;
496     textRange.chrg.cpMax = 11;
497     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
498     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
499
500     ok(!strcmp(expect3, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
501
502
503     DestroyWindow(hwndRichEdit);
504 }
505
506 static void test_EM_GETSELTEXT(void)
507 {
508     HWND hwndRichEdit = new_richedit(NULL);
509     const char * text1 = "foo bar\r\nfoo bar";
510     const char * text2 = "foo bar\rfoo bar";
511     const char * expect1 = "bar\r\nfoo";
512     const char * expect2 = "bar\rfoo";
513     char buffer[1024] = {0};
514     LRESULT result;
515
516     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
517
518     SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
519     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
520     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
521     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
522
523     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
524
525     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
526     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
527     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
528
529     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
530
531
532     DestroyWindow(hwndRichEdit);
533 }
534
535 static const char haystack[] = "WINEWine wineWine wine WineWine";
536                              /* ^0        ^10       ^20       ^30 */
537
538 static const char haystack2[] = "first\r\r\nsecond";
539
540 struct find_s {
541   int start;
542   int end;
543   const char *needle;
544   int flags;
545   int expected_loc;
546 };
547
548
549 struct find_s find_tests[] = {
550   /* Find in empty text */
551   {0, -1, "foo", FR_DOWN, -1},
552   {0, -1, "foo", 0, -1},
553   {0, -1, "", FR_DOWN, -1},
554   {20, 5, "foo", FR_DOWN, -1},
555   {5, 20, "foo", FR_DOWN, -1}
556 };
557
558 struct find_s find_tests2[] = {
559   /* No-result find */
560   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
561   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
562
563   /* Subsequent finds */
564   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
565   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
566   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
567   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
568
569   /* Find backwards */
570   {19, 20, "Wine", FR_MATCHCASE, -1},
571   {10, 20, "Wine", FR_MATCHCASE, 13},
572   {20, 10, "Wine", FR_MATCHCASE, -1},
573
574   /* Case-insensitive */
575   {1, 31, "wInE", FR_DOWN, 4},
576   {1, 31, "Wine", FR_DOWN, 4},
577
578   /* High-to-low ranges */
579   {20, 5, "Wine", FR_DOWN, -1},
580   {2, 1, "Wine", FR_DOWN, -1},
581   {30, 29, "Wine", FR_DOWN, -1},
582   {20, 5, "Wine", 0, /*13*/ -1},
583
584   /* Find nothing */
585   {5, 10, "", FR_DOWN, -1},
586   {10, 5, "", FR_DOWN, -1},
587   {0, -1, "", FR_DOWN, -1},
588   {10, 5, "", 0, -1},
589
590   /* Whole-word search */
591   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
592   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
593   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
594   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
595   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
596   {11, -1, "winewine", FR_WHOLEWORD, 23},
597   {31, -1, "winewine", FR_WHOLEWORD, -1},
598
599   /* Bad ranges */
600   {5, 200, "XXX", FR_DOWN, -1},
601   {-20, 20, "Wine", FR_DOWN, -1},
602   {-20, 20, "Wine", FR_DOWN, -1},
603   {-15, -20, "Wine", FR_DOWN, -1},
604   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
605
606   /* Check the case noted in bug 4479 where matches at end aren't recognized */
607   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
608   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
609   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
610   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
611   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
612
613   /* The backwards case of bug 4479; bounds look right
614    * Fails because backward find is wrong */
615   {19, 20, "WINE", FR_MATCHCASE, -1},
616   {0, 20, "WINE", FR_MATCHCASE, 0},
617
618   {0, -1, "wineWine wine", FR_DOWN, 0},
619   {0, -1, "wineWine wine", 0, 0},
620   {0, -1, "INEW", 0, 1},
621   {0, 31, "INEW", 0, 1},
622   {4, -1, "INEW", 0, 10},
623 };
624
625 struct find_s find_tests3[] = {
626   /* Searching for end of line characters */
627   {0, -1, "t\r\r\ns", FR_DOWN | FR_MATCHCASE, 4},
628   {6, -1, "\r\n", FR_DOWN | FR_MATCHCASE, 6},
629   {7, -1, "\n", FR_DOWN | FR_MATCHCASE, 7},
630 };
631
632 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
633   int findloc;
634   FINDTEXT ft;
635   memset(&ft, 0, sizeof(ft));
636   ft.chrg.cpMin = f->start;
637   ft.chrg.cpMax = f->end;
638   ft.lpstrText = f->needle;
639   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
640   ok(findloc == f->expected_loc,
641      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
642      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
643 }
644
645 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
646     int id) {
647   int findloc;
648   FINDTEXTEX ft;
649   int expected_end_loc;
650
651   memset(&ft, 0, sizeof(ft));
652   ft.chrg.cpMin = f->start;
653   ft.chrg.cpMax = f->end;
654   ft.lpstrText = f->needle;
655   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
656   ok(findloc == f->expected_loc,
657       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
658       name, id, f->needle, f->start, f->end, f->flags, findloc);
659   ok(ft.chrgText.cpMin == f->expected_loc,
660       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
661       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
662   expected_end_loc = ((f->expected_loc == -1) ? -1
663         : f->expected_loc + strlen(f->needle));
664   ok(ft.chrgText.cpMax == expected_end_loc,
665       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
666       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
667 }
668
669 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
670     int num_tests)
671 {
672   int i;
673
674   for (i = 0; i < num_tests; i++) {
675     if (*name == '3' && i == 0) {
676         todo_wine {
677             check_EM_FINDTEXT(hwnd, name, &find[i], i);
678             check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
679         }
680     } else {
681         check_EM_FINDTEXT(hwnd, name, &find[i], i);
682         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
683     }
684   }
685 }
686
687 static void test_EM_FINDTEXT(void)
688 {
689   HWND hwndRichEdit = new_richedit(NULL);
690
691   /* Empty rich edit control */
692   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
693       sizeof(find_tests)/sizeof(struct find_s));
694
695   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
696
697   /* Haystack text */
698   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
699       sizeof(find_tests2)/sizeof(struct find_s));
700
701   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack2);
702
703   /* Haystack text 2 (with EOL characters) */
704   run_tests_EM_FINDTEXT(hwndRichEdit, "3", find_tests3,
705       sizeof(find_tests3)/sizeof(struct find_s));
706
707   DestroyWindow(hwndRichEdit);
708 }
709
710 static void test_EM_POSFROMCHAR(void)
711 {
712   HWND hwndRichEdit = new_richedit(NULL);
713   int i;
714   POINTL pl;
715   LRESULT result;
716   unsigned int height = 0;
717   int xpos = 0;
718   static const char text[] = "aa\n"
719       "this is a long line of text that should be longer than the "
720       "control's width\n"
721       "cc\n"
722       "dd\n"
723       "ee\n"
724       "ff\n"
725       "gg\n"
726       "hh\n";
727
728   /* Fill the control to lines to ensure that most of them are offscreen */
729   for (i = 0; i < 50; i++)
730   {
731     /* Do not modify the string; it is exactly 16 characters long. */
732     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
733     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
734   }
735
736   /*
737    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
738    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
739    Richedit 3.0 accepts either of the above API conventions.
740    */
741
742   /* Testing Richedit 1.0 API format */
743
744   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
745      Since all lines are identical and drawn with the same font,
746      they should have the same height... right?
747    */
748   for (i = 0; i < 50; i++)
749   {
750     /* All the lines are 16 characters long */
751     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
752     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
753     if (i == 0)
754     {
755       ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
756       ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
757       xpos = pl.x;
758     }
759     else if (i == 1)
760     {
761       ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
762       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
763       height = pl.y;
764     }
765     else
766     {
767       ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
768       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
769     }
770   }
771
772   /* Testing position at end of text */
773   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
774   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
775   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
776   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
777
778   /* Testing position way past end of text */
779   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
780   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
781   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
782   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
783
784
785   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
786   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
787   for (i = 0; i < 50; i++)
788   {
789     /* All the lines are 16 characters long */
790     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
791     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
792     ok(pl.y == (i - 1) * height,
793         "EM_POSFROMCHAR reports y=%d, expected %d\n",
794         pl.y, (i - 1) * height);
795     ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
796   }
797
798   /* Testing position at end of text */
799   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
800   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
801   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
802   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
803
804   /* Testing position way past end of text */
805   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
806   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
807   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
808   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
809
810   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
811   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
812   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
813
814   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
815   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
816   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
817   ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
818   xpos = pl.x;
819
820   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
821   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
822   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
823   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
824   todo_wine {
825   /* Fails on builtin because horizontal scrollbar is not being shown */
826   ok(pl.x < xpos, "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", pl.x, xpos);
827   }
828   DestroyWindow(hwndRichEdit);
829 }
830
831 static void test_word_wrap(void)
832 {
833     HWND hwnd;
834     POINTL point = {0, 60}; /* This point must be below the first line */
835     const char *text = "Must be long enough to test line wrapping";
836     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
837     int res, pos, lines;
838
839     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
840      * when specified on window creation and set later. */
841     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
842                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
843     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
844     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
845     ok(res, "WM_SETTEXT failed.\n");
846     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
847     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
848     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
849     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
850
851     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
852     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
853     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
854     DestroyWindow(hwnd);
855
856     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
857                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
858     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
859
860     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
861     ok(res, "WM_SETTEXT failed.\n");
862     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
863     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
864     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
865     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
866
867     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
868     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
869     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
870     DestroyWindow(hwnd);
871
872     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
873                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
874     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
875     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
876     ok(res, "WM_SETTEXT failed.\n");
877     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
878     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
879
880     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
881     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
882     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
883     DestroyWindow(hwnd);
884
885     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
886                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
887                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
888     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
889     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
890     ok(res, "WM_SETTEXT failed.\n");
891     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
892     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
893
894     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
895     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
896     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
897
898     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
899     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
900     todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
901     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
902     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
903
904     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
905     todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
906     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
907     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
908     DestroyWindow(hwnd);
909
910     /* Test to see if wrapping happens with redraw disabled. */
911     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
912                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
913     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
914     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
915     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
916     /* redraw is disabled by making the window invisible. */
917     ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
918     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
919     ok(res, "EM_REPLACESEL failed.\n");
920     MoveWindow(hwnd, 0, 0, 100, 80, TRUE);
921     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
922     /* Wrapping didn't happen while redraw was disabled. */
923     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
924     todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
925     /* There isn't even a rewrap from resizing the window. */
926     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
927     todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
928     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
929     ok(res, "EM_REPLACESEL failed.\n");
930     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
931     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
932
933     DestroyWindow(hwnd);
934 }
935
936 static void test_EM_GETOPTIONS(void)
937 {
938     HWND hwnd;
939     DWORD options;
940
941     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
942                         WS_POPUP,
943                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
944     options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
945     ok(options == 0, "Incorrect options %x\n", options);
946     DestroyWindow(hwnd);
947
948     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
949                         WS_POPUP|WS_VSCROLL|WS_HSCROLL,
950                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
951     options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
952     ok(options == ECO_AUTOVSCROLL,
953        "Incorrect initial options %x\n", options);
954     DestroyWindow(hwnd);
955 }
956
957 static void test_autoscroll(void)
958 {
959     HWND hwnd;
960     UINT ret;
961
962     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
963      * auto vertical/horizontal scrolling options. */
964     hwnd = CreateWindowEx(0, RICHEDIT_CLASS10A, NULL,
965                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
966                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
967     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
968     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
969     ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
970     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
971     ret = GetWindowLong(hwnd, GWL_STYLE);
972     todo_wine ok(ret & ES_AUTOVSCROLL, "ES_AUTOVSCROLL isn't set.\n");
973     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
974     DestroyWindow(hwnd);
975
976     hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
977                           WS_POPUP|ES_MULTILINE,
978                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
979     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
980     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
981     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
982     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
983     ret = GetWindowLong(hwnd, GWL_STYLE);
984     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
985     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
986     DestroyWindow(hwnd);
987 }
988
989 START_TEST( editor )
990 {
991   MSG msg;
992   time_t end;
993
994   /* Must explicitly LoadLibrary(). The test has no references to functions in
995    * RICHED32.DLL, so the linker doesn't actually link to it. */
996   hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
997   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
998
999   test_WM_SETTEXT();
1000   test_EM_GETTEXTRANGE();
1001   test_EM_GETSELTEXT();
1002   test_WM_GETTEXTLENGTH();
1003   test_EM_STREAMIN();
1004   test_EM_STREAMOUT();
1005   test_EM_GETLINE();
1006   test_EM_LINELENGTH();
1007   test_EM_FINDTEXT();
1008   test_EM_POSFROMCHAR();
1009   test_word_wrap();
1010   test_EM_GETOPTIONS();
1011   test_autoscroll();
1012
1013   /* Set the environment variable WINETEST_RICHED32 to keep windows
1014    * responsive and open for 30 seconds. This is useful for debugging.
1015    *
1016    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1017    * 50ms before retrying the queue. */
1018   end = time(NULL) + 30;
1019   if (getenv( "WINETEST_RICHED32" )) {
1020     while (time(NULL) < end) {
1021       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1022         TranslateMessage(&msg);
1023         DispatchMessage(&msg);
1024       } else {
1025         Sleep(50);
1026       }
1027     }
1028   }
1029
1030   OleFlushClipboard();
1031   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1032 }