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