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