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 <stdio.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 <commdlg.h>
33 #include <richedit.h>
34 #include <time.h>
35 #include <wine/test.h>
36 
37 static HMODULE hmoduleRichEdit;
38 static BOOL is_lang_japanese;
39 
new_window(LPCSTR lpClassName,DWORD dwStyle,HWND parent)40 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
41   HWND hwnd;
42   hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
43                       |WS_VISIBLE, 0, 0, 500, 60, parent, NULL,
44                       hmoduleRichEdit, NULL);
45   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
46   return hwnd;
47 }
48 
new_richedit(HWND parent)49 static HWND new_richedit(HWND parent) {
50   return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
51 }
52 
is_rtl(void)53 static BOOL is_rtl(void) {
54   LOCALESIGNATURE sig;
55 
56   return (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
57                          (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
58           (sig.lsUsb[3] & 0x08000000) != 0);
59 }
60 
test_WM_SETTEXT(void)61 static void test_WM_SETTEXT(void)
62 {
63   static const struct {
64     const char *itemtext;
65     DWORD lines;
66     DWORD lines_rtl;
67     DWORD lines_broken;
68   } testitems[] = {
69     { "TestSomeText", 1, 1},
70     { "TestSomeText\r", 1, 1},
71     { "TestSomeText\rSomeMoreText\r", 2, 1, 1}, /* NT4 and below */
72     { "TestSomeText\n\nTestSomeText", 3, 3},
73     { "TestSomeText\r\r\nTestSomeText", 2, 2},
74     { "TestSomeText\r\r\n\rTestSomeText", 3, 2, 2}, /* NT4 and below */
75     { "TestSomeText\r\n\r\r\n\rTestSomeText", 4, 3, 3}, /* NT4 and below */
76     { "TestSomeText\r\n", 2, 2},
77     { "TestSomeText\r\nSomeMoreText\r\n", 3, 3},
78     { "TestSomeText\r\n\r\nTestSomeText", 3, 3},
79     { "TestSomeText TestSomeText", 1, 1},
80     { "TestSomeText \r\nTestSomeText", 2, 2},
81     { "TestSomeText\r\n \r\nTestSomeText", 3, 3},
82     { "TestSomeText\n", 2, 2},
83     { "TestSomeText\r\r\r", 3, 1, 1}, /* NT4 and below */
84     { "TestSomeText\r\r\rSomeMoreText", 4, 1, 1} /* NT4 and below */
85   };
86   HWND hwndRichEdit = new_richedit(NULL);
87   int i;
88   BOOL rtl = is_rtl();
89 
90   /* This test attempts to show that WM_SETTEXT on a riched32 control does not
91    * attempt to modify the text that is pasted into the control, and should
92    * return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
93    *
94    * For riched32, the rules for breaking lines seem to be the following:
95    * - \r\n is one line break. This is the normal case.
96    * - \r{0,2}\n is one line break. In particular, \n by itself is a line break.
97    * - \r{0,N-1}\r\r\n is N line breaks.
98    * - \n{1,N} are that many line breaks.
99    * - \r with text or other characters (except \n) past it, is a line break. That
100    *   is, a run of \r{N} without a terminating \n is considered N line breaks
101    * - \r at the end of the text is NOT a line break. This differs from riched20,
102    *   where \r at the end of the text is a proper line break.
103    * However, on RTL language versions, \r is simply skipped and never used
104    * for line breaking (only \n adds a line break)
105    */
106 
107   for (i = 0; i < ARRAY_SIZE(testitems); i++) {
108 
109     char buf[1024] = {0};
110     LRESULT result;
111 
112     result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testitems[i].itemtext);
113     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
114     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
115     ok (result == lstrlenA(buf),
116         "[%d] WM_GETTEXT returned %ld instead of expected %u\n",
117         i, result, lstrlenA(buf));
118     result = strcmp(testitems[i].itemtext, buf);
119     ok (result == 0,
120         "[%d] WM_SETTEXT round trip: strcmp = %ld\n", i, result);
121     result = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
122     ok (result == (rtl ? testitems[i].lines_rtl : testitems[i].lines) ||
123         broken(testitems[i].lines_broken && result == testitems[i].lines_broken),
124         "[%d] EM_GETLINECOUNT returned %ld, expected %d\n", i, result, testitems[i].lines);
125   }
126 
127   DestroyWindow(hwndRichEdit);
128 }
129 
test_WM_GETTEXTLENGTH(void)130 static void test_WM_GETTEXTLENGTH(void)
131 {
132     HWND hwndRichEdit = new_richedit(NULL);
133     static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
134     static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
135     static const char text3[] = "abcdef\x8e\xf0";
136     int result;
137 
138     /* Test for WM_GETTEXTLENGTH */
139     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
140     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
141     ok(result == lstrlenA(text1),
142        "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
143        result, lstrlenA(text1));
144 
145     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
146     result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
147     ok(result == lstrlenA(text2),
148        "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
149        result, lstrlenA(text2));
150 
151     /* Test with multibyte character */
152     if (!is_lang_japanese)
153         skip("Skip multibyte character tests on non-Japanese platform\n");
154     else
155     {
156         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
157         result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
158         ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
159     }
160 
161     DestroyWindow(hwndRichEdit);
162 }
163 
test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)164 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
165                                          LPBYTE pbBuff,
166                                          LONG cb,
167                                          LONG *pcb)
168 {
169   const char** str = (const char**)dwCookie;
170   int size = strlen(*str);
171   *pcb = cb;
172   if (*pcb > size) {
173     *pcb = size;
174   }
175   if (*pcb > 0) {
176     memcpy(pbBuff, *str, *pcb);
177     *str += *pcb;
178   }
179   return 0;
180 }
181 
182 
test_EM_STREAMIN(void)183 static void test_EM_STREAMIN(void)
184 {
185   HWND hwndRichEdit = new_richedit(NULL);
186   LRESULT result;
187   EDITSTREAM es;
188   char buffer[1024] = {0};
189 
190   const char * streamText0 = "{\\rtf1 TestSomeText}";
191   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
192   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
193 
194   const char * streamText1 =
195   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
196   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
197   "}\r\n";
198 
199   /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
200   const char * streamText2 =
201     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
202     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
203     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
204     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
205     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
206     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
207     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
208 
209   const char * streamText3 = "RichEdit1";
210 
211   /* Minimal test without \par at the end */
212   es.dwCookie = (DWORD_PTR)&streamText0;
213   es.dwError = 0;
214   es.pfnCallback = test_EM_STREAMIN_esCallback;
215   SendMessageA(hwndRichEdit, EM_STREAMIN,
216               (WPARAM)(SF_RTF), (LPARAM)&es);
217 
218   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
219   ok (result  == 12,
220       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
221   result = strcmp (buffer,"TestSomeText");
222   ok (result  == 0,
223       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
224   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
225 
226   /* Native richedit 2.0 ignores last \par */
227   es.dwCookie = (DWORD_PTR)&streamText0a;
228   es.dwError = 0;
229   es.pfnCallback = test_EM_STREAMIN_esCallback;
230   SendMessageA(hwndRichEdit, EM_STREAMIN,
231               (WPARAM)(SF_RTF), (LPARAM)&es);
232 
233   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
234   ok (result  == 12,
235       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
236   result = strcmp (buffer,"TestSomeText");
237   ok (result  == 0,
238       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
239   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
240 
241   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
242   es.dwCookie = (DWORD_PTR)&streamText0b;
243   es.dwError = 0;
244   es.pfnCallback = test_EM_STREAMIN_esCallback;
245   SendMessageA(hwndRichEdit, EM_STREAMIN,
246               (WPARAM)(SF_RTF), (LPARAM)&es);
247 
248   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
249   ok (result  == 14,
250       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
251   result = strcmp (buffer,"TestSomeText\r\n");
252   ok (result  == 0,
253       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
254   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
255 
256   es.dwCookie = (DWORD_PTR)&streamText1;
257   es.dwError = 0;
258   es.pfnCallback = test_EM_STREAMIN_esCallback;
259   SendMessageA(hwndRichEdit, EM_STREAMIN,
260               (WPARAM)(SF_RTF), (LPARAM)&es);
261 
262   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
263   ok (result  == 12,
264       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
265   result = strcmp (buffer,"TestSomeText");
266   ok (result  == 0,
267       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
268   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
269 
270 
271   es.dwCookie = (DWORD_PTR)&streamText2;
272   es.dwError = 0;
273   SendMessageA(hwndRichEdit, EM_STREAMIN,
274               (WPARAM)(SF_RTF), (LPARAM)&es);
275 
276   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
277   todo_wine {
278   ok (result  == 9,
279       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
280   }
281   result = strcmp (buffer,"RichEdit1");
282   todo_wine {
283   ok (result  == 0,
284       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
285   }
286   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
287 
288   es.dwCookie = (DWORD_PTR)&streamText3;
289   es.dwError = 0;
290   SendMessageA(hwndRichEdit, EM_STREAMIN,
291               (WPARAM)(SF_RTF), (LPARAM)&es);
292 
293   result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
294   ok (result  == 0,
295       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
296   ok (strlen(buffer)  == 0,
297       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
298   ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
299 
300   DestroyWindow(hwndRichEdit);
301 }
302 
test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,LPBYTE pbBuff,LONG cb,LONG * pcb)303 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
304                                          LPBYTE pbBuff,
305                                          LONG cb,
306                                          LONG *pcb)
307 {
308   char** str = (char**)dwCookie;
309   *pcb = cb;
310   if (*pcb > 0) {
311     memcpy(*str, pbBuff, *pcb);
312     *str += *pcb;
313   }
314   return 0;
315 }
316 
test_EM_STREAMOUT(void)317 static void test_EM_STREAMOUT(void)
318 {
319   HWND hwndRichEdit = new_richedit(NULL);
320   int r;
321   EDITSTREAM es;
322   char buf[1024] = {0};
323   char * p;
324 
325   const char * TestItem1 = "TestSomeText";
326   const char * TestItem2 = "TestSomeText\r";
327   const char * TestItem3 = "TestSomeText\r\n";
328 
329   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
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   SendMessageA(hwndRichEdit, EM_STREAMOUT,
336               (WPARAM)(SF_TEXT), (LPARAM)&es);
337   r = strlen(buf);
338   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
339   ok(strcmp(buf, TestItem1) == 0,
340         "streamed text different, got %s\n", buf);
341 
342   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
343   p = buf;
344   es.dwCookie = (DWORD_PTR)&p;
345   es.dwError = 0;
346   es.pfnCallback = test_WM_SETTEXT_esCallback;
347   memset(buf, 0, sizeof(buf));
348   SendMessageA(hwndRichEdit, EM_STREAMOUT,
349               (WPARAM)(SF_TEXT), (LPARAM)&es);
350   r = strlen(buf);
351 
352   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
353   ok(strcmp(buf, TestItem2) == 0,
354         "streamed text different, got %s\n", buf);
355 
356   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
357   p = buf;
358   es.dwCookie = (DWORD_PTR)&p;
359   es.dwError = 0;
360   es.pfnCallback = test_WM_SETTEXT_esCallback;
361   memset(buf, 0, sizeof(buf));
362   SendMessageA(hwndRichEdit, EM_STREAMOUT,
363               (WPARAM)(SF_TEXT), (LPARAM)&es);
364   r = strlen(buf);
365   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
366   ok(strcmp(buf, TestItem3) == 0,
367         "streamed text different, got %s\n", buf);
368 
369   DestroyWindow(hwndRichEdit);
370 }
371 
372 static const struct getline_s {
373   int line;
374   size_t buffer_len;
375   const char *text;
376   const char *broken_text;
377 } gl[] = {
378   {0, 10, "foo bar\r\n", "foo bar\r\n"},
379   {1, 10, "\r",          "\r\r\r\n"},
380   {2, 10, "\r\r\n",      "bar\n"},
381   {3, 10, "bar\n",       "\r\n"},
382   {4, 10, "\r\n"},
383 
384   /* Buffer smaller than line length */
385   {0, 2, "foo bar\r"},
386   {0, 1, "foo bar\r"},
387   {0, 0, "foo bar\r"}
388 };
389 
test_EM_GETLINE(void)390 static void test_EM_GETLINE(void)
391 {
392   int i;
393   HWND hwndRichEdit = new_richedit(NULL);
394   static const int nBuf = 1024;
395   char dest[1024], origdest[1024];
396   LRESULT linecount;
397   const char text[] = "foo bar\r\n"
398       "\r"
399       "\r\r\n"
400       "bar\n";
401   BOOL broken_os = FALSE;
402   BOOL rtl = is_rtl();
403 
404   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
405   linecount = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
406   if (linecount == 4)
407   {
408     broken_os = TRUE;
409     win_skip("Win9x, WinME and NT4 handle '\\r only' differently\n");
410   }
411 
412   memset(origdest, 0xBB, nBuf);
413   for (i = 0; i < ARRAY_SIZE(gl); i++)
414   {
415     int nCopied, expected_nCopied, expected_bytes_written;
416     char gl_text[1024];
417 
418     if (gl[i].line >= linecount)
419       continue; /* Win9x, WinME and NT4 */
420 
421     if (broken_os && gl[i].broken_text)
422       /* Win9x, WinME and NT4 */
423       strcpy(gl_text, gl[i].broken_text);
424     else
425       strcpy(gl_text, gl[i].text);
426 
427     expected_nCopied = min(gl[i].buffer_len, strlen(gl_text));
428     /* Cater for the fact that Win9x, WinME and NT4 don't append the '\0' */
429     expected_bytes_written = min(gl[i].buffer_len, strlen(gl_text) + (broken_os ? 0 : 1));
430 
431     memset(dest, 0xBB, nBuf);
432     *(WORD *) dest = gl[i].buffer_len;
433 
434     /* EM_GETLINE appends a "\r\0" to the end of the line
435      * nCopied counts up to and including the '\r' */
436     nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
437     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
438        expected_nCopied);
439     /* two special cases since a parameter is passed via dest */
440     if (gl[i].buffer_len == 0)
441       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
442          "buffer_len=0\n");
443     else if (gl[i].buffer_len == 1)
444       ok(dest[0] == gl_text[0] && !dest[1] &&
445          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
446     else
447     {
448       ok(!strncmp(dest, gl_text, expected_bytes_written),
449          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
450       if (! rtl || expected_bytes_written == gl[i].buffer_len)
451         ok(!strncmp(dest + expected_bytes_written, origdest
452                     + expected_bytes_written, nBuf - expected_bytes_written),
453            "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
454       else
455         ok(dest[expected_bytes_written] == 0 &&
456            !strncmp(dest + expected_bytes_written + 1, origdest
457                     + expected_bytes_written + 1, nBuf - (expected_bytes_written + 1)),
458            "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
459     }
460   }
461 
462   DestroyWindow(hwndRichEdit);
463 }
464 
test_EM_LINELENGTH(void)465 static void test_EM_LINELENGTH(void)
466 {
467   HWND hwndRichEdit = new_richedit(NULL);
468   const char * text =
469         "richedit1\r"
470         "richedit1\n"
471         "richedit1\r\n"
472         "short\r"
473         "richedit1\r"
474         "\r"
475         "\r"
476         "\r\r\n";
477   int offset_test[16][2] = {
478         {0, 9},  /* Line 1: |richedit1\r */
479         {5, 9},  /* Line 1: riche|dit1\r */
480         {10, 9}, /* Line 2: |richedit1\n */
481         {15, 9}, /* Line 2: riche|dit1\n */
482         {20, 9}, /* Line 3: |richedit1\r\n */
483         {25, 9}, /* Line 3: riche|dit1\r\n */
484         {30, 9}, /* Line 3: richedit1\r|\n */
485         {31, 5}, /* Line 4: |short\r */
486         {42, 9}, /* Line 5: riche|dit1\r */
487         {46, 9}, /* Line 5: richedit1|\r */
488         {47, 0}, /* Line 6: |\r */
489         {48, 0}, /* Line 7: |\r */
490         {49, 0}, /* Line 8: |\r\r\n */
491         {50, 0}, /* Line 8: \r|\r\n */
492         {51, 0}, /* Line 8: \r\r|\n */
493         {52, 0}, /* Line 9: \r\r\n| */
494   };
495   int i;
496   LRESULT result;
497 
498   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
499 
500   result = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
501   if (result == 4) {
502      win_skip("Win9x, WinME and NT4 don't handle '\\r only' correctly\n");
503      return;
504   }
505   ok(result == 9, "Incorrect line count of %ld\n", result);
506 
507   for (i = 0; i < ARRAY_SIZE(offset_test); i++) {
508     result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
509     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
510        offset_test[i][0], result, offset_test[i][1]);
511   }
512 
513   /* Test with multibyte character */
514   if (!is_lang_japanese)
515     skip("Skip multibyte character tests on non-Japanese platform\n");
516   else
517   {
518     const char *text1 =
519           "wine\n"
520           "richedit\x8e\xf0\n"
521           "wine";
522     static int offset_test1[3][3] = {
523            {0, 4},  /* Line 1: |wine\n */
524            {5, 10, 1}, /* Line 2: |richedit\x8e\xf0\n */
525            {16, 4}, /* Line 3: |wine */
526     };
527     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
528     for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
529       result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
530       todo_wine_if (offset_test1[i][2])
531           ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
532              offset_test1[i][0], result, offset_test1[i][1]);
533     }
534   }
535 
536   DestroyWindow(hwndRichEdit);
537 }
538 
test_EM_GETTEXTRANGE(void)539 static void test_EM_GETTEXTRANGE(void)
540 {
541     HWND hwndRichEdit = new_richedit(NULL);
542     const char * text1 = "foo bar\r\nfoo bar";
543     const char * text3 = "foo bar\rfoo bar";
544     const char * expect1 = "bar\r\nfoo";
545     const char * expect2 = "\nfoo";
546     const char * expect3 = "bar\rfoo";
547     char buffer[1024] = {0};
548     LRESULT result;
549     TEXTRANGEA textRange;
550 
551     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
552 
553     textRange.lpstrText = buffer;
554     textRange.chrg.cpMin = 4;
555     textRange.chrg.cpMax = 12;
556     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
557     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
558     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
559 
560     textRange.lpstrText = buffer;
561     textRange.chrg.cpMin = 8;
562     textRange.chrg.cpMax = 12;
563     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
564     ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
565     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
566 
567     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
568 
569     textRange.lpstrText = buffer;
570     textRange.chrg.cpMin = 4;
571     textRange.chrg.cpMax = 11;
572     result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
573     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
574 
575     ok(!strcmp(expect3, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
576 
577     /* Test with multibyte character */
578     if (!is_lang_japanese)
579         skip("Skip multibyte character tests on non-Japanese platform\n");
580     else
581     {
582         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
583         textRange.chrg.cpMin = 4;
584         textRange.chrg.cpMax = 8;
585         result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
586         ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
587         todo_wine ok(!strcmp("ef\x8e\xf0", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
588     }
589 
590     DestroyWindow(hwndRichEdit);
591 }
592 
test_EM_GETSELTEXT(void)593 static void test_EM_GETSELTEXT(void)
594 {
595     HWND hwndRichEdit = new_richedit(NULL);
596     const char * text1 = "foo bar\r\nfoo bar";
597     const char * text2 = "foo bar\rfoo bar";
598     const char * expect1 = "bar\r\nfoo";
599     const char * expect2 = "bar\rfoo";
600     char buffer[1024] = {0};
601     LRESULT result;
602 
603     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
604 
605     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 12);
606     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
607     ok(result == 8, "EM_GETSELTEXT returned %ld\n", result);
608     ok(!strcmp(expect1, buffer), "EM_GETSELTEXT filled %s\n", buffer);
609 
610     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
611 
612     SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
613     result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
614     ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
615     ok(!strcmp(expect2, buffer), "EM_GETSELTEXT filled %s\n", buffer);
616 
617     /* Test with multibyte character */
618     if (!is_lang_japanese)
619         skip("Skip multibyte character tests on non-Japanese platform\n");
620     else
621     {
622         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
623         SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
624         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
625         ok(result == 4, "EM_GETSELTEXT returned %ld\n", result);
626         todo_wine ok(!strcmp("ef\x8e\xf0", buffer), "EM_GETSELTEXT filled %s\n", buffer);
627     }
628 
629     DestroyWindow(hwndRichEdit);
630 }
631 
632 static const char haystack[] = "WINEWine wineWine wine WineWine";
633                              /* ^0        ^10       ^20       ^30 */
634 
635 static const char haystack2[] = "first\r\r\nsecond";
636 
637 struct find_s {
638   int start;
639   int end;
640   const char *needle;
641   int flags;
642   int expected_loc;
643 };
644 
645 
646 static struct find_s find_tests[] = {
647   /* Find in empty text */
648   {0, -1, "foo", FR_DOWN, -1},
649   {0, -1, "foo", 0, -1},
650   {0, -1, "", FR_DOWN, -1},
651   {20, 5, "foo", FR_DOWN, -1},
652   {5, 20, "foo", FR_DOWN, -1}
653 };
654 
655 static struct find_s find_tests2[] = {
656   /* No-result find */
657   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
658   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
659 
660   /* Subsequent finds */
661   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
662   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
663   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
664   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
665 
666   /* Find backwards */
667   {19, 20, "Wine", FR_MATCHCASE, -1},
668   {10, 20, "Wine", FR_MATCHCASE, 13},
669   {20, 10, "Wine", FR_MATCHCASE, -1},
670 
671   /* Case-insensitive */
672   {1, 31, "wInE", FR_DOWN, 4},
673   {1, 31, "Wine", FR_DOWN, 4},
674 
675   /* High-to-low ranges */
676   {20, 5, "Wine", FR_DOWN, -1},
677   {2, 1, "Wine", FR_DOWN, -1},
678   {30, 29, "Wine", FR_DOWN, -1},
679   {20, 5, "Wine", 0, /*13*/ -1},
680 
681   /* Find nothing */
682   {5, 10, "", FR_DOWN, -1},
683   {10, 5, "", FR_DOWN, -1},
684   {0, -1, "", FR_DOWN, -1},
685   {10, 5, "", 0, -1},
686 
687   /* Whole-word search */
688   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
689   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
690   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
691   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
692   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
693   {11, -1, "winewine", FR_WHOLEWORD, 23},
694   {31, -1, "winewine", FR_WHOLEWORD, -1},
695 
696   /* Bad ranges */
697   {5, 200, "XXX", FR_DOWN, -1},
698   {-20, 20, "Wine", FR_DOWN, -1},
699   {-20, 20, "Wine", FR_DOWN, -1},
700   {-15, -20, "Wine", FR_DOWN, -1},
701   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
702 
703   /* Check the case noted in bug 4479 where matches at end aren't recognized */
704   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
705   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
706   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
707   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
708   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
709 
710   /* The backwards case of bug 4479; bounds look right
711    * Fails because backward find is wrong */
712   {19, 20, "WINE", FR_MATCHCASE, -1},
713   {0, 20, "WINE", FR_MATCHCASE, 0},
714 
715   {0, -1, "wineWine wine", FR_DOWN, 0},
716   {0, -1, "wineWine wine", 0, 0},
717   {0, -1, "INEW", 0, 1},
718   {0, 31, "INEW", 0, 1},
719   {4, -1, "INEW", 0, 10},
720 };
721 
722 static struct find_s find_tests3[] = {
723   /* Searching for end of line characters */
724   {0, -1, "t\r\r\ns", FR_DOWN | FR_MATCHCASE, 4},
725   {6, -1, "\r\n", FR_DOWN | FR_MATCHCASE, 6},
726   {7, -1, "\n", FR_DOWN | FR_MATCHCASE, 7},
727 };
728 
check_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * f,int id)729 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
730   int findloc;
731   FINDTEXTA ft;
732   memset(&ft, 0, sizeof(ft));
733   ft.chrg.cpMin = f->start;
734   ft.chrg.cpMax = f->end;
735   ft.lpstrText = f->needle;
736   findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ft);
737   ok(findloc == f->expected_loc,
738      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
739      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
740 }
741 
check_EM_FINDTEXTEX(HWND hwnd,const char * name,struct find_s * f,int id)742 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
743     int id) {
744   int findloc;
745   FINDTEXTEXA ft;
746   int expected_end_loc;
747 
748   memset(&ft, 0, sizeof(ft));
749   ft.chrg.cpMin = f->start;
750   ft.chrg.cpMax = f->end;
751   ft.lpstrText = f->needle;
752   ft.chrgText.cpMax = 0xdeadbeef;
753   findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ft);
754   ok(findloc == f->expected_loc,
755       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
756       name, id, f->needle, f->start, f->end, f->flags, findloc);
757   ok(ft.chrgText.cpMin == f->expected_loc,
758       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
759       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
760   expected_end_loc = ((f->expected_loc == -1) ? -1
761         : f->expected_loc + strlen(f->needle));
762   ok(ft.chrgText.cpMax == expected_end_loc ||
763       broken(ft.chrgText.cpMin == -1 && ft.chrgText.cpMax == 0xdeadbeef), /* Win9x, WinME and NT4 */
764       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
765       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
766 }
767 
run_tests_EM_FINDTEXT(HWND hwnd,const char * name,struct find_s * find,int num_tests)768 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
769     int num_tests)
770 {
771   int i;
772 
773   for (i = 0; i < num_tests; i++) {
774     check_EM_FINDTEXT(hwnd, name, &find[i], i);
775     check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
776   }
777 }
778 
test_EM_FINDTEXT(void)779 static void test_EM_FINDTEXT(void)
780 {
781   HWND hwndRichEdit = new_richedit(NULL);
782 
783   /* Empty rich edit control */
784   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests));
785 
786   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
787 
788   /* Haystack text */
789   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2));
790 
791   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack2);
792 
793   /* Haystack text 2 (with EOL characters) */
794   run_tests_EM_FINDTEXT(hwndRichEdit, "3", find_tests3, ARRAY_SIZE(find_tests3));
795 
796   DestroyWindow(hwndRichEdit);
797 }
798 
test_EM_POSFROMCHAR(void)799 static void test_EM_POSFROMCHAR(void)
800 {
801   HWND hwndRichEdit = new_richedit(NULL);
802   int i;
803   POINTL pl;
804   LRESULT result;
805   unsigned int height = 0;
806   int xpos = 0;
807   int xpos_rtl_adjusted = 0;
808   static const char text[] = "aa\n"
809       "this is a long line of text that should be longer than the "
810       "control's width\n"
811       "cc\n"
812       "dd\n"
813       "ee\n"
814       "ff\n"
815       "gg\n"
816       "hh\n";
817 
818   /* Fill the control to lines to ensure that most of them are offscreen */
819   for (i = 0; i < 50; i++)
820   {
821     /* Do not modify the string; it is exactly 16 characters long. */
822     SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
823     SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
824   }
825 
826   /*
827    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
828    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
829    Richedit 3.0 accepts either of the above API conventions.
830    */
831 
832   /* Testing Richedit 1.0 API format */
833 
834   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
835      Since all lines are identical and drawn with the same font,
836      they should have the same height... right?
837    */
838   for (i = 0; i < 50; i++)
839   {
840     /* All the lines are 16 characters long */
841     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
842     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
843     if (i == 0)
844     {
845       ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
846       ok(pl.x == 1 ||
847           broken(pl.x == 0), /* Win9x, WinME and NT4 */
848           "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
849       xpos = pl.x;
850       xpos_rtl_adjusted = xpos + (is_rtl() ? 7 : 0);
851     }
852     else if (i == 1)
853     {
854       ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
855       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
856       height = pl.y;
857     }
858     else
859     {
860       ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
861       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
862     }
863   }
864 
865   /* Testing position at end of text */
866   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
867   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
868   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
869   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
870 
871   /* Testing position way past end of text */
872   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
873   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
874   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
875 
876   ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
877 
878 
879   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
880   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
881   for (i = 0; i < 50; i++)
882   {
883     /* All the lines are 16 characters long */
884     result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
885     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
886     ok(pl.y == (i - 1) * height,
887         "EM_POSFROMCHAR reports y=%d, expected %d\n",
888         pl.y, (i - 1) * height);
889     ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
890   }
891 
892   /* Testing position at end of text */
893   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
894   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
895   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
896   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
897 
898   /* Testing position way past end of text */
899   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
900   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
901   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
902   ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
903 
904   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
905   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
906   SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
907 
908   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
909   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
910   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
911   ok(pl.x == 1 ||
912       broken(pl.x == 0), /* Win9x, WinME and NT4 */
913       "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
914   xpos = pl.x;
915 
916   SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
917   result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
918   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
919   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
920   todo_wine {
921   /* Fails on builtin because horizontal scrollbar is not being shown */
922   ok(pl.x < xpos ||
923       broken(pl.x == xpos), /* Win9x, WinME and NT4 */
924       "EM_POSFROMCHAR reports x=%d, expected value less than %d\n", pl.x, xpos);
925   }
926   DestroyWindow(hwndRichEdit);
927 }
928 
test_word_wrap(void)929 static void test_word_wrap(void)
930 {
931     HWND hwnd;
932     POINTL point = {0, 60}; /* This point must be below the first line */
933     const char *text = "Must be long enough to test line wrapping";
934     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
935     int res, pos, lines, prevlines, reflines[3];
936 
937     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
938      * when specified on window creation and set later. */
939     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
940                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
941     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
942     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
943     ok(res, "WM_SETTEXT failed.\n");
944     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
945     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
946     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
947     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
948 
949     SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
950     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
951     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
952     DestroyWindow(hwnd);
953 
954     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
955                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
956     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
957 
958     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
959     ok(res, "WM_SETTEXT failed.\n");
960     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
961     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
962     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
963     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
964 
965     SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
966     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
967     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
968     DestroyWindow(hwnd);
969 
970     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
971                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
972     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
973     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
974     ok(res, "WM_SETTEXT failed.\n");
975     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
976     ok(!pos ||
977         broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
978         "pos=%d indicating word wrap when none is expected.\n", pos);
979     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
980     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
981 
982     SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle);
983     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
984     ok(!pos ||
985         broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
986         "pos=%d indicating word wrap when none is expected.\n", pos);
987     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
988     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
989     DestroyWindow(hwnd);
990 
991     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
992                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
993                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
994     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
995     res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
996     ok(res, "WM_SETTEXT failed.\n");
997     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
998     ok(!pos ||
999         broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1000         "pos=%d indicating word wrap when none is expected.\n", pos);
1001     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1002     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1003 
1004     SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle);
1005     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1006     ok(!pos ||
1007         broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1008         "pos=%d indicating word wrap when none is expected.\n", pos);
1009     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1010     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1011 
1012     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
1013     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
1014     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
1015     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1016     ok(!pos ||
1017         broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1018         "pos=%d indicating word wrap when none is expected.\n", pos);
1019     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1020     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1021 
1022     res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
1023     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
1024     pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1025     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
1026     DestroyWindow(hwnd);
1027 
1028     /* First lets see if the text would wrap normally (needed for reference) */
1029     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
1030                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
1031     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
1032     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
1033     res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
1034     ok(res, "EM_REPLACESEL failed.\n");
1035     /* Should have wrapped */
1036     reflines[0] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1037     ok(reflines[0] > 1, "Line was expected to wrap (%d lines).\n", reflines[0]);
1038     /* Resize the window to fit the line */
1039     MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
1040     /* Text should not be wrapped */
1041     reflines[1] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1042     ok(reflines[1] == 1, "Line wasn't expected to wrap (%d lines).\n", reflines[1]);
1043     /* Resize the window again to make sure the line wraps again */
1044     MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
1045     reflines[2] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1046     ok(reflines[2] > 1, "Line was expected to wrap (%d lines).\n", reflines[2]);
1047     DestroyWindow(hwnd);
1048 
1049     /* Same test with redraw disabled */
1050     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
1051                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
1052     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
1053     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
1054     /* Redraw is disabled by making the window invisible. */
1055     SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
1056     ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
1057     res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
1058     ok(res, "EM_REPLACESEL failed.\n");
1059     /* Should have wrapped */
1060     prevlines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1061     ok(prevlines == reflines[0],
1062         "Line was expected to wrap (%d lines).\n", prevlines);
1063     /* Resize the window to fit the line, no change to the number of lines */
1064     MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
1065     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1066     todo_wine
1067     ok(lines == prevlines ||
1068         broken(lines == reflines[1]), /* Win98, WinME and NT4 */
1069         "Expected no change in the number of lines\n");
1070     /* Resize the window again to make sure the line wraps again */
1071     MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
1072     lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1073     todo_wine
1074     ok(lines == prevlines ||
1075         broken(lines == reflines[2]), /* Win98, WinME and NT4 */
1076         "Expected no change in the number of lines\n");
1077     DestroyWindow(hwnd);
1078 }
1079 
test_EM_GETOPTIONS(void)1080 static void test_EM_GETOPTIONS(void)
1081 {
1082     HWND hwnd;
1083     DWORD options;
1084 
1085     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
1086                         WS_POPUP,
1087                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1088     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1089     ok(options == 0, "Incorrect options %x\n", options);
1090     DestroyWindow(hwnd);
1091 
1092     hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
1093                         WS_POPUP|WS_VSCROLL|WS_HSCROLL,
1094                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1095     options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1096     ok(options == ECO_AUTOVSCROLL ||
1097        broken(options == 0), /* Win9x, WinME and NT4 */
1098        "Incorrect initial options %x\n", options);
1099     DestroyWindow(hwnd);
1100 }
1101 
test_autoscroll(void)1102 static void test_autoscroll(void)
1103 {
1104     HWND hwnd;
1105     UINT ret;
1106 
1107     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
1108      * auto vertical/horizontal scrolling options. */
1109     hwnd = CreateWindowExA(0, RICHEDIT_CLASS10A, NULL,
1110                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
1111                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1112     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1113     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1114     ok(ret & ECO_AUTOVSCROLL ||
1115         broken(!(ret & ECO_AUTOVSCROLL)), /* Win9x, WinME and NT4 */
1116         "ECO_AUTOVSCROLL isn't set.\n");
1117     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1118     ret = GetWindowLongA(hwnd, GWL_STYLE);
1119     todo_wine
1120     ok(ret & ES_AUTOVSCROLL ||
1121         broken(!(ret & ES_AUTOVSCROLL)), /* Win9x, WinMe and NT4 */
1122         "ES_AUTOVSCROLL isn't set.\n");
1123     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1124     DestroyWindow(hwnd);
1125 
1126     hwnd = CreateWindowExA(0, RICHEDIT_CLASS10A, NULL,
1127                           WS_POPUP|ES_MULTILINE,
1128                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1129     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1130     ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1131     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
1132     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1133     ret = GetWindowLongA(hwnd, GWL_STYLE);
1134     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
1135     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1136     DestroyWindow(hwnd);
1137 }
1138 
simulate_typing_characters(HWND hwnd,const char * szChars)1139 static void simulate_typing_characters(HWND hwnd, const char* szChars)
1140 {
1141     int ret;
1142 
1143     while (*szChars != '\0') {
1144         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
1145         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
1146         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
1147         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
1148         szChars++;
1149     }
1150 }
1151 
format_test_result(char * target,const char * src)1152 static void format_test_result(char *target, const char *src)
1153 {
1154     int i;
1155     for (i = 0; i < strlen(src); i++)
1156         sprintf(target + 2*i, "%02x", src[i] & 0xFF);
1157     target[2*i] = 0;
1158 }
1159 
1160 /*
1161  * This test attempts to show the effect of enter on a richedit
1162  * control v1.0 inserts CRLF whereas for higher versions it only
1163  * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
1164  * and also shows that GT_USECRLF has no effect in richedit 1.0, but
1165  * does for higher. The same test is cloned in riched32 and riched20.
1166  * Also shows the difference between WM_CHAR/WM_KEYDOWN in v1.0 and higher versions
1167  */
test_enter(void)1168 static void test_enter(void)
1169 {
1170   static const struct {
1171     const char *initialtext;
1172     const int   cursor;
1173     const char *expectedtext;
1174   } testenteritems[] = {
1175     { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n"},
1176     { "aaabbb\r\n", 6, "aaabbb\r\n\r\n"},
1177     { "aa\rabbb\r\n", 7, "aa\rabbb\r\n\r\n"},
1178     { "aa\rabbb\r\n", 3, "aa\r\r\nabbb\r\n"},
1179     { "aa\rabbb\r\n", 2, "aa\r\n\rabbb\r\n"}
1180   };
1181 
1182   char expectedbuf[1024];
1183   char resultbuf[1024];
1184   HWND hwndRichEdit = new_richedit(NULL);
1185   UINT i;
1186   char buf[1024] = {0};
1187   GETTEXTEX getText = {sizeof(buf)};
1188   LRESULT result;
1189   const char *expected;
1190 
1191   for (i = 0; i < ARRAY_SIZE(testenteritems); i++)
1192   {
1193     /* Set the text to the initial text */
1194     result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
1195     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1196 
1197     /* Send Enter */
1198     SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
1199     simulate_typing_characters(hwndRichEdit, "\r");
1200 
1201     /* 1. Retrieve with WM_GETTEXT */
1202     buf[0] = 0x00;
1203     result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
1204     expected = testenteritems[i].expectedtext;
1205 
1206     format_test_result(resultbuf, buf);
1207     format_test_result(expectedbuf, expected);
1208 
1209     result = strcmp(expected, buf);
1210     ok (result == 0,
1211         "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
1212         i, resultbuf, expectedbuf);
1213 
1214     /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
1215     getText.flags = GT_DEFAULT;
1216     getText.codepage = CP_ACP;
1217     buf[0] = 0x00;
1218     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1219     expected = testenteritems[i].expectedtext;
1220 
1221     format_test_result(resultbuf, buf);
1222     format_test_result(expectedbuf, expected);
1223 
1224     result = strcmp(expected, buf);
1225     ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1226         "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
1227         i, resultbuf, expectedbuf);
1228 
1229     /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
1230     getText.flags = GT_USECRLF;
1231     getText.codepage = CP_ACP;
1232     buf[0] = 0x00;
1233     result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1234     expected = testenteritems[i].expectedtext;
1235 
1236     format_test_result(resultbuf, buf);
1237     format_test_result(expectedbuf, expected);
1238 
1239     result = strcmp(expected, buf);
1240     ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1241         "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
1242         i, resultbuf, expectedbuf);
1243   }
1244 
1245   /* Show that WM_CHAR is handled differently from WM_KEYDOWN */
1246   getText.flags    = GT_DEFAULT;
1247   getText.codepage = CP_ACP;
1248 
1249   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1250   ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1251   SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
1252   SendMessageW(hwndRichEdit, WM_KEYDOWN, VK_RETURN, 0);
1253 
1254   result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1255   ok(result == 1, "Got %d\n", (int)result);
1256   format_test_result(resultbuf, buf);
1257   format_test_result(expectedbuf, "T");
1258   result = strcmp(resultbuf, expectedbuf);
1259   ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
1260 
1261   result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1262   ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1263   SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
1264   SendMessageW(hwndRichEdit, WM_CHAR, '\r', 0);
1265 
1266   result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1267   ok(result == 3, "Got %ld\n", result);
1268   format_test_result(resultbuf, buf);
1269   format_test_result(expectedbuf, "T\r\n");
1270   result = strcmp(resultbuf, expectedbuf);
1271   ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
1272 
1273   DestroyWindow(hwndRichEdit);
1274 }
1275 
1276 struct exsetsel_s {
1277   LONG min;
1278   LONG max;
1279   LRESULT expected_retval;
1280   int expected_getsel_start;
1281   int expected_getsel_end;
1282   BOOL result_todo;
1283   BOOL sel_todo;
1284 };
1285 
1286 static const struct exsetsel_s exsetsel_tests[] = {
1287   /* sanity tests */
1288   {5, 10, 10, 5, 10, 0, 0 },
1289   {15, 17, 17, 15, 17, 0, 0 },
1290   /* test cpMax > strlen() */
1291   {0, 100, 19, 0, 19, 1, 0 },
1292   /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
1293   {-1, 1, 17, 17, 17, 1, 0 },
1294   /* test cpMin == cpMax */
1295   {5, 5, 5, 5, 5, 0, 0 },
1296   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1297   {-1, 0, 5, 5, 5, 0, 0 },
1298   {-1, 17, 5, 5, 5, 0, 0 },
1299   {-1, 18, 5, 5, 5, 0, 0 },
1300   /* test cpMin < 0 && cpMax < 0 */
1301   {-1, -1, 17, 17, 17, 0, 0 },
1302   {-4, -5, 17, 17, 17, 0, 0 },
1303   /* test cpMin >=0 && cpMax < 0 (bug 6814) */
1304   {0, -1, 19, 0, 19, 1, 0 },
1305   {17, -5, 19, 17, 19, 1, 0 },
1306   {18, -3, 19, 17, 19, 1, 1 },
1307   /* test if cpMin > cpMax */
1308   {15, 19, 19, 15, 19, 1, 0 },
1309   {19, 15, 19, 15, 19, 1, 0 },
1310   /* cpMin == strlen() && cpMax > cpMin */
1311   {17, 18, 17, 17, 17, 1, 1 },
1312   {17, 50, 19, 17, 19, 1, 0 },
1313 };
1314 
check_EM_EXSETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)1315 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1316     CHARRANGE cr;
1317     LRESULT result;
1318     int start, end;
1319 
1320     cr.cpMin = setsel->min;
1321     cr.cpMax = setsel->max;
1322     result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
1323 
1324     todo_wine_if (setsel->result_todo)
1325         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1326 
1327     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
1328 
1329     todo_wine_if (setsel->sel_todo)
1330         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end,
1331            "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
1332            id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
1333 }
1334 
test_EM_EXSETSEL(void)1335 static void test_EM_EXSETSEL(void)
1336 {
1337     HWND hwndRichEdit = new_richedit(NULL);
1338     int i;
1339     const int num_tests = ARRAY_SIZE(exsetsel_tests);
1340 
1341     /* sending some text to the window */
1342     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
1343     /*                                                 01234567890123456 */
1344 
1345     for (i = 0; i < num_tests; i++) {
1346         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1347     }
1348 
1349     if (!is_lang_japanese)
1350         skip("Skip multibyte character tests on non-Japanese platform\n");
1351     else
1352     {
1353         CHARRANGE cr;
1354         LRESULT result;
1355 #define MAX_BUF_LEN 1024
1356         char bufA[MAX_BUF_LEN] = {0};
1357 
1358         /* Test with multibyte character */
1359         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1360         /*                                                 012345  6   7 8901 */
1361         cr.cpMin = 4; cr.cpMax = 8;
1362         result =  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1363         todo_wine ok(result == 7, "EM_EXSETSEL return %ld expected 7\n", result);
1364         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
1365         todo_wine ok(!strcmp(bufA, "ef\x8e\xf0"), "EM_GETSELTEXT return incorrect string\n");
1366         SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1367         ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
1368         ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
1369     }
1370 
1371     DestroyWindow(hwndRichEdit);
1372 }
1373 
check_EM_SETSEL(HWND hwnd,const struct exsetsel_s * setsel,int id)1374 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1375     LRESULT result;
1376     int start, end;
1377 
1378     result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
1379 
1380     todo_wine_if (setsel->result_todo)
1381         ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1382 
1383     SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
1384 
1385     todo_wine_if (setsel->sel_todo)
1386         ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end,
1387            "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
1388            id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
1389 }
1390 
test_EM_SETSEL(void)1391 static void test_EM_SETSEL(void)
1392 {
1393     char buffA[32] = {0};
1394     HWND hwndRichEdit = new_richedit(NULL);
1395     int i;
1396     const int num_tests = ARRAY_SIZE(exsetsel_tests);
1397 
1398     /* sending some text to the window */
1399     SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
1400     /*                                                 01234567890123456 */
1401 
1402     for (i = 0; i < num_tests; i++) {
1403         check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1404     }
1405 
1406     SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
1407     buffA[0] = 123;
1408     SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
1409     ok(buffA[0] == 0, "selection text %s\n", buffA);
1410 
1411     if (!is_lang_japanese)
1412         skip("Skip multibyte character tests on non-Japanese platform\n");
1413     else
1414     {
1415         int sel_start, sel_end;
1416         LRESULT result;
1417 
1418         /* Test with multibyte character */
1419         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1420         /*                                                 012345  6   7 8901 */
1421         result =  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1422         todo_wine ok(result == 7, "EM_SETSEL return %ld expected 7\n", result);
1423         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
1424         todo_wine ok(!strcmp(buffA, "ef\x8e\xf0"), "EM_GETSELTEXT return incorrect string\n");
1425         result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
1426         ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
1427         ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
1428     }
1429 
1430     DestroyWindow(hwndRichEdit);
1431 }
1432 
START_TEST(editor)1433 START_TEST( editor )
1434 {
1435   MSG msg;
1436   time_t end;
1437   BOOL ret;
1438 
1439   /* Must explicitly LoadLibrary(). The test has no references to functions in
1440    * RICHED32.DLL, so the linker doesn't actually link to it. */
1441   hmoduleRichEdit = LoadLibraryA("riched32.dll");
1442   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1443   is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
1444 
1445   test_WM_SETTEXT();
1446   test_EM_GETTEXTRANGE();
1447   test_EM_GETSELTEXT();
1448   test_WM_GETTEXTLENGTH();
1449   test_EM_STREAMIN();
1450   test_EM_STREAMOUT();
1451   test_EM_GETLINE();
1452   test_EM_LINELENGTH();
1453   test_EM_FINDTEXT();
1454   test_EM_POSFROMCHAR();
1455   test_word_wrap();
1456   test_EM_GETOPTIONS();
1457   test_autoscroll();
1458   test_enter();
1459   test_EM_EXSETSEL();
1460   test_EM_SETSEL();
1461 
1462   /* Set the environment variable WINETEST_RICHED32 to keep windows
1463    * responsive and open for 30 seconds. This is useful for debugging.
1464    *
1465    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1466    * 50ms before retrying the queue. */
1467   end = time(NULL) + 30;
1468   if (getenv( "WINETEST_RICHED32" )) {
1469     while (time(NULL) < end) {
1470       if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
1471         TranslateMessage(&msg);
1472         DispatchMessageA(&msg);
1473       } else {
1474         Sleep(50);
1475       }
1476     }
1477   }
1478 
1479   OleFlushClipboard();
1480   ret = FreeLibrary(hmoduleRichEdit);
1481   ok(ret, "error: %u\n", GetLastError());
1482 }
1483