ReactOS  0.4.14-dev-583-g2a1ba2c
editor.c
Go to the documentation of this file.
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22 
23 #define COBJMACROS
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <wingdi.h>
31 #include <winuser.h>
32 #include <winnls.h>
33 #include <ole2.h>
34 #include <richedit.h>
35 #include <richole.h>
36 #include <commdlg.h>
37 #include <time.h>
38 #include <wine/test.h>
39 
40 #define ID_RICHEDITTESTDBUTTON 0x123
41 
43 
44 #define ok_w3(format, szString1, szString2, szString3) \
45  WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
46  WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
47  WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
48  ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
49  format, string1, string2, string3);
50 
53 
54 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
55  HWND hwnd;
56  hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
57  |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
59  ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60  return hwnd;
61 }
62 
63 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
64  HWND hwnd;
65  hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
66  |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
68  ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
69  return hwnd;
70 }
71 
74 }
75 
78 }
79 
82 }
83 
84 /* Keeps the window reponsive for the deley_time in seconds.
85  * This is useful for debugging a test to see what is happening. */
86 static void keep_responsive(time_t delay_time)
87 {
88  MSG msg;
89  time_t end;
90 
91  /* The message pump uses PeekMessage() to empty the queue and then
92  * sleeps for 50ms before retrying the queue. */
93  end = time(NULL) + delay_time;
94  while (time(NULL) < end) {
95  if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
98  } else {
99  Sleep(50);
100  }
101  }
102 }
103 
104 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 {
106  int ret;
107 
108  while (*szChars != '\0') {
109  SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
110  ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
111  ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
112  SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
113  szChars++;
114  }
115 }
116 
117 static BOOL hold_key(int vk)
118 {
119  BYTE key_state[256];
120  BOOL result;
121 
122  result = GetKeyboardState(key_state);
123  ok(result, "GetKeyboardState failed.\n");
124  if (!result) return FALSE;
125  key_state[vk] |= 0x80;
126  result = SetKeyboardState(key_state);
127  ok(result, "SetKeyboardState failed.\n");
128  return result != 0;
129 }
130 
131 static BOOL release_key(int vk)
132 {
133  BYTE key_state[256];
134  BOOL result;
135 
136  result = GetKeyboardState(key_state);
137  ok(result, "GetKeyboardState failed.\n");
138  if (!result) return FALSE;
139  key_state[vk] &= ~0x80;
140  result = SetKeyboardState(key_state);
141  ok(result, "SetKeyboardState failed.\n");
142  return result != 0;
143 }
144 
145 static const char haystack[] = "WINEWine wineWine wine WineWine";
146  /* ^0 ^10 ^20 ^30 */
147 
148 struct find_s {
149  int start;
150  int end;
151  const char *needle;
152  int flags;
154 };
155 
156 
157 static struct find_s find_tests[] = {
158  /* Find in empty text */
159  {0, -1, "foo", FR_DOWN, -1},
160  {0, -1, "foo", 0, -1},
161  {0, -1, "", FR_DOWN, -1},
162  {20, 5, "foo", FR_DOWN, -1},
163  {5, 20, "foo", FR_DOWN, -1}
164 };
165 
166 static struct find_s find_tests2[] = {
167  /* No-result find */
168  {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
169  {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 
171  /* Subsequent finds */
172  {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
173  {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
174  {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
175  {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176 
177  /* Find backwards */
178  {19, 20, "Wine", FR_MATCHCASE, 13},
179  {10, 20, "Wine", FR_MATCHCASE, 4},
180  {20, 10, "Wine", FR_MATCHCASE, 13},
181 
182  /* Case-insensitive */
183  {1, 31, "wInE", FR_DOWN, 4},
184  {1, 31, "Wine", FR_DOWN, 4},
185 
186  /* High-to-low ranges */
187  {20, 5, "Wine", FR_DOWN, -1},
188  {2, 1, "Wine", FR_DOWN, -1},
189  {30, 29, "Wine", FR_DOWN, -1},
190  {20, 5, "Wine", 0, 13},
191 
192  /* Find nothing */
193  {5, 10, "", FR_DOWN, -1},
194  {10, 5, "", FR_DOWN, -1},
195  {0, -1, "", FR_DOWN, -1},
196  {10, 5, "", 0, -1},
197 
198  /* Whole-word search */
199  {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
200  {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
201  {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
202  {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
203  {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
204  {11, -1, "winewine", FR_WHOLEWORD, 0},
205  {31, -1, "winewine", FR_WHOLEWORD, 23},
206 
207  /* Bad ranges */
208  {5, 200, "XXX", FR_DOWN, -1},
209  {-20, 20, "Wine", FR_DOWN, -1},
210  {-20, 20, "Wine", FR_DOWN, -1},
211  {-15, -20, "Wine", FR_DOWN, -1},
212  {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 
214  /* Check the case noted in bug 4479 where matches at end aren't recognized */
215  {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
216  {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217  {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218  {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
219  {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 
221  /* The backwards case of bug 4479; bounds look right
222  * Fails because backward find is wrong */
223  {19, 20, "WINE", FR_MATCHCASE, 0},
224  {0, 20, "WINE", FR_MATCHCASE, -1},
225 
226  {0, -1, "wineWine wine", 0, -1},
227 };
228 
229 static WCHAR *atowstr(const char *str)
230 {
231  WCHAR *ret;
232  DWORD len;
233  len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
234  ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
235  MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
236  return ret;
237 }
238 
239 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
240 {
241  int findloc;
242 
243  if(unicode){
244  FINDTEXTW ftw;
245  memset(&ftw, 0, sizeof(ftw));
246  ftw.chrg.cpMin = f->start;
247  ftw.chrg.cpMax = f->end;
248  ftw.lpstrText = atowstr(f->needle);
249 
250  findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
251  ok(findloc == f->expected_loc,
252  "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
253  name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254 
255  findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
256  ok(findloc == f->expected_loc,
257  "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
258  name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
259 
260  HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
261  }else{
262  FINDTEXTA fta;
263  memset(&fta, 0, sizeof(fta));
264  fta.chrg.cpMin = f->start;
265  fta.chrg.cpMax = f->end;
266  fta.lpstrText = f->needle;
267 
268  findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
269  ok(findloc == f->expected_loc,
270  "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
271  name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
272  }
273 }
274 
275 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
276  int id, BOOL unicode)
277 {
278  int findloc;
279  int expected_end_loc;
280 
281  if(unicode){
282  FINDTEXTEXW ftw;
283  memset(&ftw, 0, sizeof(ftw));
284  ftw.chrg.cpMin = f->start;
285  ftw.chrg.cpMax = f->end;
286  ftw.lpstrText = atowstr(f->needle);
287  findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
288  ok(findloc == f->expected_loc,
289  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
290  name, id, f->needle, f->start, f->end, f->flags, findloc);
291  ok(ftw.chrgText.cpMin == f->expected_loc,
292  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
293  name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
294  expected_end_loc = ((f->expected_loc == -1) ? -1
295  : f->expected_loc + strlen(f->needle));
296  ok(ftw.chrgText.cpMax == expected_end_loc,
297  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
298  name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
299  HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
300  }else{
301  FINDTEXTEXA fta;
302  memset(&fta, 0, sizeof(fta));
303  fta.chrg.cpMin = f->start;
304  fta.chrg.cpMax = f->end;
305  fta.lpstrText = f->needle;
306  findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
307  ok(findloc == f->expected_loc,
308  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
309  name, id, f->needle, f->start, f->end, f->flags, findloc);
310  ok(fta.chrgText.cpMin == f->expected_loc,
311  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
312  name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
313  expected_end_loc = ((f->expected_loc == -1) ? -1
314  : f->expected_loc + strlen(f->needle));
315  ok(fta.chrgText.cpMax == expected_end_loc,
316  "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
317  name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
318  }
319 }
320 
321 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
322  int num_tests, BOOL unicode)
323 {
324  int i;
325 
326  for (i = 0; i < num_tests; i++) {
327  check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
328  check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
329  }
330 }
331 
332 static void test_EM_FINDTEXT(BOOL unicode)
333 {
334  HWND hwndRichEdit;
335  CHARFORMAT2A cf2;
336 
337  if(unicode)
338  hwndRichEdit = new_richeditW(NULL);
339  else
340  hwndRichEdit = new_richedit(NULL);
341 
342  /* Empty rich edit control */
343  run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests), unicode);
344 
345  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
346 
347  /* Haystack text */
348  run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2), unicode);
349 
350  /* Setting a format on an arbitrary range should have no effect in search
351  results. This tests correct offset reporting across runs. */
352  cf2.cbSize = sizeof(CHARFORMAT2A);
353  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
354  cf2.dwMask = CFM_ITALIC | cf2.dwMask;
355  cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
356  SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
357  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
358 
359  /* Haystack text, again */
360  run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
361 
362  /* Yet another range */
363  cf2.dwMask = CFM_BOLD | cf2.dwMask;
364  cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
365  SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
366  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
367 
368  /* Haystack text, again */
369  run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
370 
371  DestroyWindow(hwndRichEdit);
372 }
373 
374 static const struct getline_s {
375  int line;
376  size_t buffer_len;
377  const char *text;
378 } gl[] = {
379  {0, 10, "foo bar\r"},
380  {1, 10, "\r"},
381  {2, 10, "bar\r"},
382  {3, 10, "\r"},
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 
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  const char text[] = "foo bar\n"
397  "\n"
398  "bar\n";
399 
400  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
401 
402  memset(origdest, 0xBB, nBuf);
403  for (i = 0; i < ARRAY_SIZE(gl); i++)
404  {
405  int nCopied;
406  int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
407  int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
408  memset(dest, 0xBB, nBuf);
409  *(WORD *) dest = gl[i].buffer_len;
410 
411  /* EM_GETLINE appends a "\r\0" to the end of the line
412  * nCopied counts up to and including the '\r' */
413  nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
414  ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
415  expected_nCopied);
416  /* two special cases since a parameter is passed via dest */
417  if (gl[i].buffer_len == 0)
418  ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
419  "buffer_len=0\n");
420  else if (gl[i].buffer_len == 1)
421  ok(dest[0] == gl[i].text[0] && !dest[1] &&
422  !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
423  else
424  {
425  /* Prepare hex strings of buffers to dump on failure. */
426  char expectedbuf[1024];
427  char resultbuf[1024];
428  int j;
429  resultbuf[0] = '\0';
430  for (j = 0; j < 32; j++)
431  sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
432  expectedbuf[0] = '\0';
433  for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
434  sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
435  for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
436  sprintf(expectedbuf+strlen(expectedbuf), "??");
437  for (; j < 32; j++) /* Bytes after declared buffer size */
438  sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
439 
440  /* Test the part of the buffer that is expected to be written according
441  * to the MSDN documentation fo EM_GETLINE, which does not state that
442  * a NULL terminating character will be added unless no text is copied.
443  *
444  * Windows NT does not append a NULL terminating character, but
445  * Windows 2000 and up do append a NULL terminating character if there
446  * is space in the buffer. The test will ignore this difference. */
447  ok(!strncmp(dest, gl[i].text, expected_bytes_written),
448  "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
449  i, expected_bytes_written, expectedbuf, resultbuf);
450  /* Test the part of the buffer after the declared length to make sure
451  * there are no buffer overruns. */
452  ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
453  nBuf - gl[i].buffer_len),
454  "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
455  i, expected_bytes_written, expectedbuf, resultbuf);
456  }
457  }
458 
459  DestroyWindow(hwndRichEdit);
460 }
461 
462 static void test_EM_LINELENGTH(void)
463 {
464  HWND hwndRichEdit = new_richedit(NULL);
465  const char * text =
466  "richedit1\r"
467  "richedit1\n"
468  "richedit1\r\n"
469  "richedit1";
470  int offset_test[10][2] = {
471  {0, 9},
472  {5, 9},
473  {10, 9},
474  {15, 9},
475  {20, 9},
476  {25, 9},
477  {30, 9},
478  {35, 9},
479  {40, 0},
480  {45, 0},
481  };
482  int i;
483  LRESULT result;
484 
485  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
486 
487  for (i = 0; i < 10; i++) {
488  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
489  ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
490  offset_test[i][0], result, offset_test[i][1]);
491  }
492 
493  /* Test with multibyte character */
494  if (!is_lang_japanese)
495  skip("Skip multibyte character tests on non-Japanese platform\n");
496  else
497  {
498  const char *text1 =
499  "wine\n"
500  "richedit\x8e\xf0\n"
501  "wine";
502  int offset_test1[3][2] = {
503  {0, 4}, /* Line 1: |wine\n */
504  {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
505  {15, 4}, /* Line 3: |wine */
506  };
507  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
508  for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
509  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
510  ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
511  offset_test1[i][0], result, offset_test1[i][1]);
512  }
513  }
514 
515  DestroyWindow(hwndRichEdit);
516 }
517 
519 {
520  POINT p = {-1, -1};
522  ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
523  return p.y;
524 }
525 
526 static void move_cursor(HWND hwnd, LONG charindex)
527 {
528  CHARRANGE cr;
529  cr.cpMax = charindex;
530  cr.cpMin = charindex;
531  SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
532 }
533 
534 static void line_scroll(HWND hwnd, int amount)
535 {
536  SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
537 }
538 
539 static void test_EM_SCROLLCARET(void)
540 {
541  int prevY, curY;
542  const char text[] = "aa\n"
543  "this is a long line of text that should be longer than the "
544  "control's width\n"
545  "cc\n"
546  "dd\n"
547  "ee\n"
548  "ff\n"
549  "gg\n"
550  "hh\n";
551  /* The richedit window height needs to be large enough vertically to fit in
552  * more than two lines of text, so the new_richedit function can't be used
553  * since a height of 60 was not large enough on some systems.
554  */
555  HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
557  0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
558  ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
559 
560  /* Can't verify this */
561  SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
562 
563  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
564 
565  /* Caret above visible window */
566  line_scroll(hwndRichEdit, 3);
567  prevY = get_scroll_pos_y(hwndRichEdit);
568  SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
569  curY = get_scroll_pos_y(hwndRichEdit);
570  ok(prevY != curY, "%d == %d\n", prevY, curY);
571 
572  /* Caret below visible window */
573  move_cursor(hwndRichEdit, sizeof(text) - 1);
574  line_scroll(hwndRichEdit, -3);
575  prevY = get_scroll_pos_y(hwndRichEdit);
576  SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
577  curY = get_scroll_pos_y(hwndRichEdit);
578  ok(prevY != curY, "%d == %d\n", prevY, curY);
579 
580  /* Caret in visible window */
581  move_cursor(hwndRichEdit, sizeof(text) - 2);
582  prevY = get_scroll_pos_y(hwndRichEdit);
583  SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
584  curY = get_scroll_pos_y(hwndRichEdit);
585  ok(prevY == curY, "%d != %d\n", prevY, curY);
586 
587  /* Caret still in visible window */
588  line_scroll(hwndRichEdit, -1);
589  prevY = get_scroll_pos_y(hwndRichEdit);
590  SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
591  curY = get_scroll_pos_y(hwndRichEdit);
592  ok(prevY == curY, "%d != %d\n", prevY, curY);
593 
594  DestroyWindow(hwndRichEdit);
595 }
596 
597 static void test_EM_POSFROMCHAR(void)
598 {
599  HWND hwndRichEdit = new_richedit(NULL);
600  int i, expected;
601  LRESULT result;
602  unsigned int height = 0;
603  int xpos = 0;
604  POINTL pt;
605  LOCALESIGNATURE sig;
606  BOOL rtl;
608  static const char text[] = "aa\n"
609  "this is a long line of text that should be longer than the "
610  "control's width\n"
611  "cc\n"
612  "dd\n"
613  "ee\n"
614  "ff\n"
615  "gg\n"
616  "hh\n";
617 
619  (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
620  (sig.lsUsb[3] & 0x08000000) != 0);
621 
622  /* Fill the control to lines to ensure that most of them are offscreen */
623  for (i = 0; i < 50; i++)
624  {
625  /* Do not modify the string; it is exactly 16 characters long. */
626  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
627  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
628  }
629 
630  /*
631  Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
632  Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
633  Richedit 3.0 accepts either of the above API conventions.
634  */
635 
636  /* Testing Richedit 2.0 API format */
637 
638  /* Testing start of lines. X-offset should be constant on all cases (native is 1).
639  Since all lines are identical and drawn with the same font,
640  they should have the same height... right?
641  */
642  for (i = 0; i < 50; i++)
643  {
644  /* All the lines are 16 characters long */
645  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
646  if (i == 0)
647  {
648  ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
649  ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
650  xpos = LOWORD(result);
651  }
652  else if (i == 1)
653  {
654  ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
655  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
656  height = HIWORD(result);
657  }
658  else
659  {
661  ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
662  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
663  }
664  }
665 
666  /* Testing position at end of text */
667  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
668  ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
669  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
670 
671  /* Testing position way past end of text */
672  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
673  ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
674  expected = (rtl ? 8 : 1);
675  ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
676 
677  /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
678  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
679  for (i = 0; i < 50; i++)
680  {
681  /* All the lines are 16 characters long */
682  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
683  ok((signed short)(HIWORD(result)) == (i - 1) * height,
684  "EM_POSFROMCHAR reports y=%hd, expected %d\n",
685  (signed short)(HIWORD(result)), (i - 1) * height);
686  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
687  }
688 
689  /* Testing position at end of text */
690  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
691  ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
692  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
693 
694  /* Testing position way past end of text */
695  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
696  ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
697  expected = (rtl ? 8 : 1);
698  ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
699 
700  /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
701  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
702  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
703 
704  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
705  ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
706  ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
707  xpos = LOWORD(result);
708 
709  SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
710  result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
711  ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
712  ok((signed short)(LOWORD(result)) < xpos,
713  "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
714  (signed short)(LOWORD(result)), xpos);
715  SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
716 
717  /* Test around end of text that doesn't end in a newline. */
718  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
719  SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720  SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
721  ok(pt.x > 1, "pt.x = %d\n", pt.x);
722  xpos = pt.x;
723  SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724  SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
725  ok(pt.x > xpos, "pt.x = %d\n", pt.x);
726  xpos = (rtl ? pt.x + 7 : pt.x);
727  SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
728  SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
729  ok(pt.x == xpos, "pt.x = %d\n", pt.x);
730 
731  /* Try a negative position. */
732  SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
733  ok(pt.x == 1, "pt.x = %d\n", pt.x);
734 
735  /* test negative indentation */
736  SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
737  (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
738  SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
739  ok(pt.x == 1, "pt.x = %d\n", pt.x);
740 
741  fmt.cbSize = sizeof(fmt);
742  SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
743  ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
744  ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
745  ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
746 
747  DestroyWindow(hwndRichEdit);
748 }
749 
750 static void test_EM_SETCHARFORMAT(void)
751 {
752  HWND hwndRichEdit = new_richedit(NULL);
753  CHARFORMAT2A cf2;
754  CHARFORMAT2W cfW;
755  CHARFORMATA cf1a;
756  CHARFORMATW cf1w;
757  int rc = 0;
758  int tested_effects[] = {
759  CFE_BOLD,
760  CFE_ITALIC,
764  CFE_LINK,
767  0
768  };
769  int i;
770  CHARRANGE cr;
771  LOCALESIGNATURE sig;
772  BOOL rtl;
773  DWORD expect_effects;
774 
776  (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
777  (sig.lsUsb[3] & 0x08000000) != 0);
778 
779  /* check charformat defaults */
780  memset(&cf2, 0, sizeof(CHARFORMAT2A));
781  cf2.cbSize = sizeof(CHARFORMAT2A);
782  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
783  ok(cf2.dwMask == CFM_ALL2, "got %08x\n", cf2.dwMask);
784  expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
785  if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
786  ok(cf2.dwEffects == expect_effects, "got %08x\n", cf2.dwEffects);
787  ok(cf2.yOffset == 0, "got %d\n", cf2.yOffset);
788  ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
789  ok(cf2.lcid == GetSystemDefaultLCID(), "got %x\n", cf2.lcid);
790  ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
791  ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
792  ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
793  ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
794 
795  /* Invalid flags, CHARFORMAT2 structure blanked out */
796  memset(&cf2, 0, sizeof(cf2));
797  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
798  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
799 
800  /* A valid flag, CHARFORMAT2 structure blanked out */
801  memset(&cf2, 0, sizeof(cf2));
802  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
803  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
804 
805  /* A valid flag, CHARFORMAT2 structure blanked out */
806  memset(&cf2, 0, sizeof(cf2));
807  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
808  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
809 
810  /* A valid flag, CHARFORMAT2 structure blanked out */
811  memset(&cf2, 0, sizeof(cf2));
812  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
813  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
814 
815  /* A valid flag, CHARFORMAT2 structure blanked out */
816  memset(&cf2, 0, sizeof(cf2));
817  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
818  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
819 
820  /* Invalid flags, CHARFORMAT2 structure minimally filled */
821  memset(&cf2, 0, sizeof(cf2));
822  cf2.cbSize = sizeof(CHARFORMAT2A);
823  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
824  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
825  rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
826  ok(rc == FALSE, "Should not be able to undo here.\n");
827  SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
828 
829  /* A valid flag, CHARFORMAT2 structure minimally filled */
830  memset(&cf2, 0, sizeof(cf2));
831  cf2.cbSize = sizeof(CHARFORMAT2A);
832  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
833  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834  rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
835  ok(rc == FALSE, "Should not be able to undo here.\n");
836  SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
837 
838  /* A valid flag, CHARFORMAT2 structure minimally filled */
839  memset(&cf2, 0, sizeof(cf2));
840  cf2.cbSize = sizeof(CHARFORMAT2A);
841  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
842  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
843  rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
844  ok(rc == FALSE, "Should not be able to undo here.\n");
845  SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
846 
847  /* A valid flag, CHARFORMAT2 structure minimally filled */
848  memset(&cf2, 0, sizeof(cf2));
849  cf2.cbSize = sizeof(CHARFORMAT2A);
850  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
851  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
852  rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
853  todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
854  SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
855 
856  /* A valid flag, CHARFORMAT2 structure minimally filled */
857  memset(&cf2, 0, sizeof(cf2));
858  cf2.cbSize = sizeof(CHARFORMAT2A);
859  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
860  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
861  rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
862  ok(rc == TRUE, "Should not be able to undo here.\n");
863  SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
864 
865  cf2.cbSize = sizeof(CHARFORMAT2A);
866  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
867 
868  /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
869  cf2.cbSize = sizeof(CHARFORMAT2A);
870  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
871  cf2.dwMask = CFM_ITALIC | cf2.dwMask;
872  cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
873 
874  /* wParam==0 is default char format, does not set modify */
875  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
876  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
877  ok(rc == 0, "Text marked as modified, expected not modified!\n");
878  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
879  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
880  if (! rtl)
881  {
882  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
883  ok(rc == 0, "Text marked as modified, expected not modified!\n");
884  }
885  else
886  skip("RTL language found\n");
887 
888  /* wParam==SCF_SELECTION sets modify if nonempty selection */
889  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
890  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
891  ok(rc == 0, "Text marked as modified, expected not modified!\n");
892  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
893  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
894  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
895  ok(rc == 0, "Text marked as modified, expected not modified!\n");
896 
897  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
899  ok(rc == 0, "Text marked as modified, expected not modified!\n");
900  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
902  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
903  ok(rc == 0, "Text marked as modified, expected not modified!\n");
904  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
905  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
906  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
907  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
908  ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
909 
910  /* wParam==SCF_ALL sets modify regardless of whether text is present */
911  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
912  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
913  ok(rc == 0, "Text marked as modified, expected not modified!\n");
914  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
915  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
916  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
917  ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
918 
919  DestroyWindow(hwndRichEdit);
920 
921  /* EM_GETCHARFORMAT tests */
922  for (i = 0; tested_effects[i]; i++)
923  {
924  hwndRichEdit = new_richedit(NULL);
925  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
926 
927  /* Need to set a TrueType font to get consistent CFM_BOLD results */
928  memset(&cf2, 0, sizeof(CHARFORMAT2A));
929  cf2.cbSize = sizeof(CHARFORMAT2A);
930  cf2.dwMask = CFM_FACE|CFM_WEIGHT;
931  cf2.dwEffects = 0;
932  strcpy(cf2.szFaceName, "Courier New");
933  cf2.wWeight = FW_DONTCARE;
934  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
935 
936  memset(&cf2, 0, sizeof(CHARFORMAT2A));
937  cf2.cbSize = sizeof(CHARFORMAT2A);
938  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
939  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
940  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
941  (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
942  ||
943  (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
944  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
945  ok((cf2.dwEffects & tested_effects[i]) == 0,
946  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
947 
948  memset(&cf2, 0, sizeof(CHARFORMAT2A));
949  cf2.cbSize = sizeof(CHARFORMAT2A);
950  cf2.dwMask = tested_effects[i];
951  if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952  cf2.dwMask = CFM_SUPERSCRIPT;
953  cf2.dwEffects = tested_effects[i];
954  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
955  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
956 
957  memset(&cf2, 0, sizeof(CHARFORMAT2A));
958  cf2.cbSize = sizeof(CHARFORMAT2A);
959  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962  (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
963  ||
964  (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966  ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
967  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
968 
969  memset(&cf2, 0, sizeof(CHARFORMAT2A));
970  cf2.cbSize = sizeof(CHARFORMAT2A);
971  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974  (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975  ||
976  (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978  ok((cf2.dwEffects & tested_effects[i]) == 0,
979  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980 
981  memset(&cf2, 0, sizeof(CHARFORMAT2A));
982  cf2.cbSize = sizeof(CHARFORMAT2A);
983  SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986  (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
987  ||
988  (cf2.dwMask & tested_effects[i]) == 0),
989  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990 
991  DestroyWindow(hwndRichEdit);
992  }
993 
994  for (i = 0; tested_effects[i]; i++)
995  {
996  hwndRichEdit = new_richedit(NULL);
997  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
998 
999  /* Need to set a TrueType font to get consistent CFM_BOLD results */
1000  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1001  cf2.cbSize = sizeof(CHARFORMAT2A);
1002  cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1003  cf2.dwEffects = 0;
1004  strcpy(cf2.szFaceName, "Courier New");
1005  cf2.wWeight = FW_DONTCARE;
1006  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1007 
1008  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1009  cf2.cbSize = sizeof(CHARFORMAT2A);
1010  cf2.dwMask = tested_effects[i];
1011  if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1012  cf2.dwMask = CFM_SUPERSCRIPT;
1013  cf2.dwEffects = tested_effects[i];
1014  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1015  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016 
1017  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018  cf2.cbSize = sizeof(CHARFORMAT2A);
1019  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1020  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022  (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1023  ||
1024  (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1025  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1026  ok((cf2.dwEffects & tested_effects[i]) == 0,
1027  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1028 
1029  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1030  cf2.cbSize = sizeof(CHARFORMAT2A);
1031  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1032  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1033  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1034  (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1035  ||
1036  (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1037  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1038  ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1039  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1040 
1041  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1042  cf2.cbSize = sizeof(CHARFORMAT2A);
1043  SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1044  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1045  ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1046  (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1047  ||
1048  (cf2.dwMask & tested_effects[i]) == 0),
1049  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1050  ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1051  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1052 
1053  DestroyWindow(hwndRichEdit);
1054  }
1055 
1056  /* Effects applied on an empty selection should take effect when selection is
1057  replaced with text */
1058  hwndRichEdit = new_richedit(NULL);
1059  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1061 
1062  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063  cf2.cbSize = sizeof(CHARFORMAT2A);
1064  cf2.dwMask = CFM_BOLD;
1065  cf2.dwEffects = CFE_BOLD;
1066  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067 
1068  /* Selection is now nonempty */
1069  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1070 
1071  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1072  cf2.cbSize = sizeof(CHARFORMAT2A);
1073  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1074  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1075 
1076  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1077  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1078  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1079  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1080 
1081 
1082  /* Set two effects on an empty selection */
1083  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1084  /* first clear bold, italic */
1085  SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1086  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1087  cf2.cbSize = sizeof(CHARFORMAT2A);
1088  cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1089  cf2.dwEffects = 0;
1090  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1091 
1092  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1093 
1094  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1095  cf2.cbSize = sizeof(CHARFORMAT2A);
1096  cf2.dwMask = CFM_BOLD;
1097  cf2.dwEffects = CFE_BOLD;
1098  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099  cf2.dwMask = CFM_ITALIC;
1100  cf2.dwEffects = CFE_ITALIC;
1101  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1102 
1103  /* Selection is now nonempty */
1104  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1105 
1106  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1107  cf2.cbSize = sizeof(CHARFORMAT2A);
1108  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1109  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1110 
1111  ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1112  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1113  ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1114  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1115 
1116  /* Setting the (empty) selection to exactly the same place as before should
1117  NOT clear the insertion style! */
1118  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1119  /* first clear bold, italic */
1120  SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1121  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1122  cf2.cbSize = sizeof(CHARFORMAT2A);
1123  cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1124  cf2.dwEffects = 0;
1125  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1126 
1127  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1128 
1129  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130  cf2.cbSize = sizeof(CHARFORMAT2A);
1131  cf2.dwMask = CFM_BOLD;
1132  cf2.dwEffects = CFE_BOLD;
1133  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1134 
1135  /* Empty selection in same place, insert style should NOT be forgotten here. */
1136  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1137 
1138  /* Selection is now nonempty */
1139  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1140 
1141  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1142  cf2.cbSize = sizeof(CHARFORMAT2A);
1143  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1144  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1145 
1146  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1147  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1148  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1149  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1150 
1151  /* Moving the selection will clear the insertion style */
1152  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1153  /* first clear bold, italic */
1154  SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1155  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1156  cf2.cbSize = sizeof(CHARFORMAT2A);
1157  cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1158  cf2.dwEffects = 0;
1159  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1160 
1161  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1162 
1163  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1164  cf2.cbSize = sizeof(CHARFORMAT2A);
1165  cf2.dwMask = CFM_BOLD;
1166  cf2.dwEffects = CFE_BOLD;
1167  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1168 
1169  /* Move selection and then put it back, insert style should be forgotten here. */
1170  SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1171  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1172 
1173  /* Selection is now nonempty */
1174  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1175 
1176  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1177  cf2.cbSize = sizeof(CHARFORMAT2A);
1178  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1179  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1180 
1181  ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1182  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1183  ok((cf2.dwEffects & CFE_BOLD) == 0,
1184  "%d, cf2.dwEffects == 0x%08x not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1185 
1186  /* Ditto with EM_EXSETSEL */
1187  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1188  /* first clear bold, italic */
1189  SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1190  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1191  cf2.cbSize = sizeof(CHARFORMAT2A);
1192  cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1193  cf2.dwEffects = 0;
1194  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1195 
1196  cr.cpMin = 2; cr.cpMax = 2;
1197  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1198 
1199  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1200  cf2.cbSize = sizeof(CHARFORMAT2A);
1201  cf2.dwMask = CFM_BOLD;
1202  cf2.dwEffects = CFE_BOLD;
1203  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1204 
1205  /* Empty selection in same place, insert style should NOT be forgotten here. */
1206  cr.cpMin = 2; cr.cpMax = 2;
1207  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1208 
1209  /* Selection is now nonempty */
1210  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1211 
1212  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1213  cf2.cbSize = sizeof(CHARFORMAT2A);
1214  cr.cpMin = 2; cr.cpMax = 6;
1215  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1216  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1217 
1218  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1219  "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1220  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1221  "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1222 
1223  /* show that wWeight is at the correct offset in CHARFORMAT2A */
1224  memset(&cf2, 0, sizeof(cf2));
1225  cf2.cbSize = sizeof(cf2);
1226  cf2.dwMask = CFM_WEIGHT;
1227  cf2.wWeight = 100;
1228  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1229  memset(&cf2, 0, sizeof(cf2));
1230  cf2.cbSize = sizeof(cf2);
1231  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1232  ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1233 
1234  memset(&cf2, 0, sizeof(cf2));
1235  cf2.cbSize = sizeof(cf2);
1236  cf2.dwMask = CFM_SPACING;
1237  cf2.sSpacing = 10;
1238  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1239  memset(&cf2, 0, sizeof(cf2));
1240  cf2.cbSize = sizeof(cf2);
1241  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1242  ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1243 
1244  /* show that wWeight is at the correct offset in CHARFORMAT2W */
1245  memset(&cfW, 0, sizeof(cfW));
1246  cfW.cbSize = sizeof(cfW);
1247  cfW.dwMask = CFM_WEIGHT;
1248  cfW.wWeight = 100;
1249  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1250  memset(&cfW, 0, sizeof(cfW));
1251  cfW.cbSize = sizeof(cfW);
1252  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1253  ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1254 
1255  memset(&cfW, 0, sizeof(cfW));
1256  cfW.cbSize = sizeof(cfW);
1257  cfW.dwMask = CFM_SPACING;
1258  cfW.sSpacing = 10;
1259  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1260  memset(&cfW, 0, sizeof(cfW));
1261  cfW.cbSize = sizeof(cfW);
1262  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1263  ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1264 
1265  /* test CFE_UNDERLINE and bUnderlineType interaction */
1266  /* clear bold, italic */
1267  SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1268  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1269  cf2.cbSize = sizeof(CHARFORMAT2A);
1270  cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1271  cf2.dwEffects = 0;
1272  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273 
1274  /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1275  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1276  cf2.cbSize = sizeof(CHARFORMAT2A);
1277  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1279  "got %08x\n", cf2.dwMask);
1280  ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1281  ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1282 
1283  /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1284  cf2.dwMask = CFM_UNDERLINETYPE;
1285  cf2.bUnderlineType = CFU_UNDERLINE;
1286  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1287  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1288  cf2.cbSize = sizeof(CHARFORMAT2A);
1289  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1291  "got %08x\n", cf2.dwMask);
1292  ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1293  ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1294 
1295  /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1296  cf2.dwMask = CFM_UNDERLINETYPE;
1297  cf2.bUnderlineType = CFU_UNDERLINENONE;
1298  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1299  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1300  cf2.cbSize = sizeof(CHARFORMAT2A);
1301  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1303  "got %08x\n", cf2.dwMask);
1304  ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1305  ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1306 
1307  /* another underline type also sets CFE_UNDERLINE */
1308  cf2.dwMask = CFM_UNDERLINETYPE;
1309  cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1310  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1311  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1312  cf2.cbSize = sizeof(CHARFORMAT2A);
1313  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1315  "got %08x\n", cf2.dwMask);
1316  ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1317  ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1318 
1319  /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1320  cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1321  cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1322  cf2.dwEffects = 0;
1323  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1324  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1325  cf2.cbSize = sizeof(CHARFORMAT2A);
1326  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1328  "got %08x\n", cf2.dwMask);
1329  ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08x\n", cf2.dwEffects);
1330  ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1331 
1332  /* And turing it back on again by just setting CFE_UNDERLINE */
1333  cf2.dwMask = CFM_UNDERLINE;
1334  cf2.dwEffects = CFE_UNDERLINE;
1335  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1336  memset(&cf2, 0, sizeof(CHARFORMAT2A));
1337  cf2.cbSize = sizeof(CHARFORMAT2A);
1338  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1340  "got %08x\n", cf2.dwMask);
1341  ok(cf2.dwEffects & CFE_UNDERLINE, "got %08x\n", cf2.dwEffects);
1342  ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1343 
1344  /* Check setting CFM_ALL2/CFM_EFFECTS2 in CHARFORMAT(A/W). */
1345  memset(&cf1a, 0, sizeof(CHARFORMATA));
1346  memset(&cf1w, 0, sizeof(CHARFORMATW));
1347  cf1a.cbSize = sizeof(CHARFORMATA);
1348  cf1w.cbSize = sizeof(CHARFORMATW);
1349  cf1a.dwMask = cf1w.dwMask = CFM_ALL2;
1350  cf1a.dwEffects = cf1w.dwEffects = CFM_EFFECTS2;
1351  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1352  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1353  /* flags only valid for CHARFORMAT2 should be masked out */
1354  ok((cf1a.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1355  ok((cf1a.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1356  SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1357  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1358  ok((cf1w.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1359  ok((cf1w.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1360 
1361  DestroyWindow(hwndRichEdit);
1362 }
1363 
1364 static void test_EM_SETTEXTMODE(void)
1365 {
1366  HWND hwndRichEdit = new_richedit(NULL);
1367  CHARFORMAT2A cf2, cf2test;
1368  CHARRANGE cr;
1369  int rc = 0;
1370 
1371  /*Attempt to use mutually exclusive modes*/
1372  rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1373  ok(rc == E_INVALIDARG,
1374  "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1375 
1376  /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1377  /*Insert text into the control*/
1378 
1379  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1380 
1381  /*Attempt to change the control to plain text mode*/
1382  rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1383  ok(rc == E_UNEXPECTED,
1384  "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1385 
1386  /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1387  If rich text is pasted, it should have the same formatting as the rest
1388  of the text in the control*/
1389 
1390  /*Italicize the text
1391  *NOTE: If the default text was already italicized, the test will simply
1392  reverse; in other words, it will copy a regular "wine" into a plain
1393  text window that uses an italicized format*/
1394  cf2.cbSize = sizeof(CHARFORMAT2A);
1395  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1396 
1397  cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1398  cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1399 
1400  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1401  ok(rc == 0, "Text marked as modified, expected not modified!\n");
1402 
1403  /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1404  however, SCF_ALL has been implemented*/
1405  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1406  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1407 
1408  rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1409  ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1410 
1411  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1412 
1413  /*Select the string "wine"*/
1414  cr.cpMin = 0;
1415  cr.cpMax = 4;
1416  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1417 
1418  /*Copy the italicized "wine" to the clipboard*/
1419  SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1420 
1421  /*Reset the formatting to default*/
1422  cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1423  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1424  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1425 
1426  /*Clear the text in the control*/
1427  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1428 
1429  /*Switch to Plain Text Mode*/
1430  rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1431  ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1432 
1433  /*Input "wine" again in normal format*/
1434  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1435 
1436  /*Paste the italicized "wine" into the control*/
1437  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1438 
1439  /*Select a character from the first "wine" string*/
1440  cr.cpMin = 2;
1441  cr.cpMax = 3;
1442  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1443 
1444  /*Retrieve its formatting*/
1445  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1446 
1447  /*Select a character from the second "wine" string*/
1448  cr.cpMin = 5;
1449  cr.cpMax = 6;
1450  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1451 
1452  /*Retrieve its formatting*/
1453  cf2test.cbSize = sizeof(CHARFORMAT2A);
1454  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1455 
1456  /*Compare the two formattings*/
1457  ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1458  "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1459  cf2.dwEffects, cf2test.dwEffects);
1460  /*Test TM_RICHTEXT by: switching back to Rich Text mode
1461  printing "wine" in the current format(normal)
1462  pasting "wine" from the clipboard(italicized)
1463  comparing the two formats(should differ)*/
1464 
1465  /*Attempt to switch with text in control*/
1466  rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1467  ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1468 
1469  /*Clear control*/
1470  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1471 
1472  /*Switch into Rich Text mode*/
1473  rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1474  ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1475 
1476  /*Print "wine" in normal formatting into the control*/
1477  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1478 
1479  /*Paste italicized "wine" into the control*/
1480  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1481 
1482  /*Select text from the first "wine" string*/
1483  cr.cpMin = 1;
1484  cr.cpMax = 3;
1485  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1486 
1487  /*Retrieve its formatting*/
1488  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1489 
1490  /*Select text from the second "wine" string*/
1491  cr.cpMin = 6;
1492  cr.cpMax = 7;
1493  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1494 
1495  /*Retrieve its formatting*/
1496  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1497 
1498  /*Test that the two formattings are not the same*/
1499  todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1500  "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1501  cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1502 
1503  DestroyWindow(hwndRichEdit);
1504 }
1505 
1506 static void test_SETPARAFORMAT(void)
1507 {
1508  HWND hwndRichEdit = new_richedit(NULL);
1509  PARAFORMAT2 fmt;
1510  HRESULT ret;
1511  LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1512  fmt.cbSize = sizeof(PARAFORMAT2);
1513  fmt.dwMask = PFM_ALIGNMENT;
1514  fmt.wAlignment = PFA_LEFT;
1515 
1516  ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1517  ok(ret != 0, "expected non-zero got %d\n", ret);
1518 
1519  fmt.cbSize = sizeof(PARAFORMAT2);
1520  fmt.dwMask = -1;
1521  ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1522  /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1523  * between richedit different native builds of riched20.dll
1524  * used on different Windows versions. */
1526  fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1527 
1528  ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1529  ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1530 
1531  /* Test some other paraformat field defaults */
1532  ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1533  ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1534  ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1535  ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1536 
1537  DestroyWindow(hwndRichEdit);
1538 }
1539 
1540 static void test_TM_PLAINTEXT(void)
1541 {
1542  /*Tests plain text properties*/
1543 
1544  HWND hwndRichEdit = new_richedit(NULL);
1545  CHARFORMAT2A cf2, cf2test;
1546  CHARRANGE cr;
1547  int rc = 0;
1548 
1549  /*Switch to plain text mode*/
1550 
1551  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1552  SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1553 
1554  /*Fill control with text*/
1555 
1556  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1557 
1558  /*Select some text and bold it*/
1559 
1560  cr.cpMin = 10;
1561  cr.cpMax = 20;
1562  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1563  cf2.cbSize = sizeof(CHARFORMAT2A);
1564  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1565 
1566  cf2.dwMask = CFM_BOLD | cf2.dwMask;
1567  cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1568 
1569  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1570  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1571 
1572  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1573  ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1574 
1575  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1576  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1577 
1578  /*Get the formatting of those characters*/
1579 
1580  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1581 
1582  /*Get the formatting of some other characters*/
1583  cf2test.cbSize = sizeof(CHARFORMAT2A);
1584  cr.cpMin = 21;
1585  cr.cpMax = 30;
1586  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1587  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1588 
1589  /*Test that they are the same as plain text allows only one formatting*/
1590 
1591  ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1592  "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1593  cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1594 
1595  /*Fill the control with a "wine" string, which when inserted will be bold*/
1596 
1597  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1598 
1599  /*Copy the bolded "wine" string*/
1600 
1601  cr.cpMin = 0;
1602  cr.cpMax = 4;
1603  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1604  SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1605 
1606  /*Swap back to rich text*/
1607 
1608  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1609  SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1610 
1611  /*Set the default formatting to bold italics*/
1612 
1613  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1614  cf2.dwMask |= CFM_ITALIC;
1615  cf2.dwEffects ^= CFE_ITALIC;
1616  rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1617  ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1618 
1619  /*Set the text in the control to "wine", which will be bold and italicized*/
1620 
1621  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1622 
1623  /*Paste the plain text "wine" string, which should take the insert
1624  formatting, which at the moment is bold italics*/
1625 
1626  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1627 
1628  /*Select the first "wine" string and retrieve its formatting*/
1629 
1630  cr.cpMin = 1;
1631  cr.cpMax = 3;
1632  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1633  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1634 
1635  /*Select the second "wine" string and retrieve its formatting*/
1636 
1637  cr.cpMin = 5;
1638  cr.cpMax = 7;
1639  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1640  SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1641 
1642  /*Compare the two formattings. They should be the same.*/
1643 
1644  ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1645  "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1646  cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1647  DestroyWindow(hwndRichEdit);
1648 }
1649 
1650 static void test_WM_GETTEXT(void)
1651 {
1652  HWND hwndRichEdit = new_richedit(NULL);
1653  static const char text[] = "Hello. My name is RichEdit!";
1654  static const char text2[] = "Hello. My name is RichEdit!\r";
1655  static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1656  char buffer[1024] = {0};
1657  int result;
1658 
1659  /* Baseline test with normal-sized buffer */
1660  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1661  result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1662  ok(result == lstrlenA(buffer),
1663  "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1664  SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1665  result = strcmp(buffer,text);
1666  ok(result == 0,
1667  "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1668 
1669  /* Test for returned value of WM_GETTEXTLENGTH */
1670  result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1671  ok(result == lstrlenA(text),
1672  "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1673  result, lstrlenA(text));
1674 
1675  /* Test for behavior in overflow case */
1676  memset(buffer, 0, 1024);
1677  result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1678  ok(result == 0 ||
1679  result == lstrlenA(text) - 1, /* XP, win2k3 */
1680  "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1681  result = strcmp(buffer,text);
1682  if (result)
1683  result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1684  ok(result == 0,
1685  "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1686 
1687  /* Baseline test with normal-sized buffer and carriage return */
1688  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1689  result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1690  ok(result == lstrlenA(buffer),
1691  "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1692  result = strcmp(buffer,text2_after);
1693  ok(result == 0,
1694  "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1695 
1696  /* Test for returned value of WM_GETTEXTLENGTH */
1697  result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1698  ok(result == lstrlenA(text2_after),
1699  "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1700  result, lstrlenA(text2_after));
1701 
1702  /* Test for behavior of CRLF conversion in case of overflow */
1703  memset(buffer, 0, 1024);
1704  result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1705  ok(result == 0 ||
1706  result == lstrlenA(text2) - 1, /* XP, win2k3 */
1707  "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1708  result = strcmp(buffer,text2);
1709  if (result)
1710  result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1711  ok(result == 0,
1712  "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1713 
1714  DestroyWindow(hwndRichEdit);
1715 }
1716 
1717 static void test_EM_GETTEXTRANGE(void)
1718 {
1719  HWND hwndRichEdit = new_richedit(NULL);
1720  const char * text1 = "foo bar\r\nfoo bar";
1721  const char * text2 = "foo bar\rfoo bar";
1722  const char * expect = "bar\rfoo";
1723  char buffer[1024] = {0};
1724  LRESULT result;
1725  TEXTRANGEA textRange;
1726 
1727  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1728 
1729  textRange.lpstrText = buffer;
1730  textRange.chrg.cpMin = 4;
1731  textRange.chrg.cpMax = 11;
1732  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1733  ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1734  ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1735 
1736  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1737 
1738  textRange.lpstrText = buffer;
1739  textRange.chrg.cpMin = 4;
1740  textRange.chrg.cpMax = 11;
1741  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1742  ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1743  ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1744 
1745  /* cpMax of text length is used instead of -1 in this case */
1746  textRange.lpstrText = buffer;
1747  textRange.chrg.cpMin = 0;
1748  textRange.chrg.cpMax = -1;
1749  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1750  ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1751  ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1752 
1753  /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1754  textRange.lpstrText = buffer;
1755  textRange.chrg.cpMin = -1;
1756  textRange.chrg.cpMax = 1;
1757  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1758  ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1759  ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1760 
1761  /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1762  textRange.lpstrText = buffer;
1763  textRange.chrg.cpMin = 1;
1764  textRange.chrg.cpMax = -1;
1765  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1766  ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1767  ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1768 
1769  /* no end character is copied if cpMax - cpMin < 0 */
1770  textRange.lpstrText = buffer;
1771  textRange.chrg.cpMin = 5;
1772  textRange.chrg.cpMax = 5;
1773  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1774  ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1775  ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1776 
1777  /* cpMax of text length is used if cpMax > text length*/
1778  textRange.lpstrText = buffer;
1779  textRange.chrg.cpMin = 0;
1780  textRange.chrg.cpMax = 1000;
1781  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1782  ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1783  ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1784 
1785  /* Test with multibyte character */
1786  if (!is_lang_japanese)
1787  skip("Skip multibyte character tests on non-Japanese platform\n");
1788  else
1789  {
1790  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1791  textRange.chrg.cpMin = 4;
1792  textRange.chrg.cpMax = 8;
1793  result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1794  todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1795  todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1796  }
1797 
1798  DestroyWindow(hwndRichEdit);
1799 }
1800 
1801 static void test_EM_GETSELTEXT(void)
1802 {
1803  HWND hwndRichEdit = new_richedit(NULL);
1804  const char * text1 = "foo bar\r\nfoo bar";
1805  const char * text2 = "foo bar\rfoo bar";
1806  const char * expect = "bar\rfoo";
1807  char buffer[1024] = {0};
1808  LRESULT result;
1809 
1810  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1811 
1812  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1813  result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1814  ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1815  ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1816 
1817  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1818 
1819  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1820  result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1821  ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1822  ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1823 
1824  /* Test with multibyte character */
1825  if (!is_lang_japanese)
1826  skip("Skip multibyte character tests on non-Japanese platform\n");
1827  else
1828  {
1829  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1830  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1831  result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1832  todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1833  todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1834  }
1835 
1836  DestroyWindow(hwndRichEdit);
1837 }
1838 
1839 /* FIXME: need to test unimplemented options and robustly test wparam */
1840 static void test_EM_SETOPTIONS(void)
1841 {
1842  HWND hwndRichEdit;
1843  static const char text[] = "Hello. My name is RichEdit!";
1844  char buffer[1024] = {0};
1845  DWORD dwStyle, options, oldOptions;
1849 
1850  /* Test initial options. */
1851  hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1852  0, 0, 200, 60, NULL, NULL,
1854  ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1855  RICHEDIT_CLASS20A, (int) GetLastError());
1856  options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1857  ok(options == 0, "Incorrect initial options %x\n", options);
1858  DestroyWindow(hwndRichEdit);
1859 
1860  hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1862  0, 0, 200, 60, NULL, NULL,
1864  ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1865  RICHEDIT_CLASS20A, (int) GetLastError());
1866  options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1867  /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1869  "Incorrect initial options %x\n", options);
1870 
1871  /* NEGATIVE TESTING - NO OPTIONS SET */
1872  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1873  SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1874 
1875  /* testing no readonly by sending 'a' to the control*/
1876  SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1877  SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1878  ok(buffer[0]=='a',
1879  "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1880  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1881 
1882  /* READONLY - sending 'a' to the control */
1883  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1885  SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1886  SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1887  ok(buffer[0]==text[0],
1888  "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1889 
1890  /* EM_SETOPTIONS changes the window style, but changing the
1891  * window style does not change the options. */
1892  dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1893  ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1894  SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1895  options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1896  ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1897  /* Confirm that the text is still read only. */
1898  SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1899  SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1900  ok(buffer[0]==text[0],
1901  "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1902 
1903  oldOptions = options;
1904  SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1905  options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1906  ok(options == oldOptions,
1907  "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1908 
1909  DestroyWindow(hwndRichEdit);
1910 }
1911 
1912 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1913 {
1914  CHARFORMAT2A text_format;
1915  text_format.cbSize = sizeof(text_format);
1916  SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1918  return (text_format.dwEffects & CFE_LINK) != 0;
1919 }
1920 
1921 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1922 {
1923  BOOL link_present = FALSE;
1924 
1925  link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1926  if (is_url)
1927  { /* control text is url; should get CFE_LINK */
1928  ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1929  }
1930  else
1931  {
1932  ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1933  }
1934 }
1935 
1937  return new_window("Static", 0, parent);
1938 }
1939 
1940 static void test_EM_AUTOURLDETECT(void)
1941 {
1942  /* DO NOT change the properties of the first two elements. To shorten the
1943  tests, all tests after WM_SETTEXT test just the first two elements -
1944  one non-URL and one URL */
1945  struct urls_s {
1946  const char *text;
1947  BOOL is_url;
1948  } urls[12] = {
1949  {"winehq.org", FALSE},
1950  {"http://www.winehq.org", TRUE},
1951  {"http//winehq.org", FALSE},
1952  {"ww.winehq.org", FALSE},
1953  {"www.winehq.org", TRUE},
1954  {"ftp://192.168.1.1", TRUE},
1955  {"ftp//192.168.1.1", FALSE},
1956  {"mailto:your@email.com", TRUE},
1957  {"prospero:prosperoserver", TRUE},
1958  {"telnet:test", TRUE},
1959  {"news:newserver", TRUE},
1960  {"wais:waisserver", TRUE}
1961  };
1962 
1963  int i, j;
1964  int urlRet=-1;
1965  HWND hwndRichEdit, parent;
1966 
1967  /* All of the following should cause the URL to be detected */
1968  const char * templates_delim[] = {
1969  "This is some text with X on it",
1970  "This is some text with (X) on it",
1971  "This is some text with X\r on it",
1972  "This is some text with ---X--- on it",
1973  "This is some text with \"X\" on it",
1974  "This is some text with 'X' on it",
1975  "This is some text with 'X' on it",
1976  "This is some text with :X: on it",
1977 
1978  "This text ends with X",
1979 
1980  "This is some text with X) on it",
1981  "This is some text with X--- on it",
1982  "This is some text with X\" on it",
1983  "This is some text with X' on it",
1984  "This is some text with X: on it",
1985 
1986  "This is some text with (X on it",
1987  "This is some text with \rX on it",
1988  "This is some text with ---X on it",
1989  "This is some text with \"X on it",
1990  "This is some text with 'X on it",
1991  "This is some text with :X on it",
1992  };
1993  /* None of these should cause the URL to be detected */
1994  const char * templates_non_delim[] = {
1995  "This is some text with |X| on it",
1996  "This is some text with *X* on it",
1997  "This is some text with /X/ on it",
1998  "This is some text with +X+ on it",
1999  "This is some text with %X% on it",
2000  "This is some text with #X# on it",
2001  "This is some text with @X@ on it",
2002  "This is some text with \\X\\ on it",
2003  "This is some text with |X on it",
2004  "This is some text with *X on it",
2005  "This is some text with /X on it",
2006  "This is some text with +X on it",
2007  "This is some text with %X on it",
2008  "This is some text with #X on it",
2009  "This is some text with @X on it",
2010  "This is some text with \\X on it",
2011  "This is some text with _X on it",
2012  };
2013  /* All of these cause the URL detection to be extended by one more byte,
2014  thus demonstrating that the tested character is considered as part
2015  of the URL. */
2016  const char * templates_xten_delim[] = {
2017  "This is some text with X| on it",
2018  "This is some text with X* on it",
2019  "This is some text with X/ on it",
2020  "This is some text with X+ on it",
2021  "This is some text with X% on it",
2022  "This is some text with X# on it",
2023  "This is some text with X@ on it",
2024  "This is some text with X\\ on it",
2025  "This is some text with X_ on it",
2026  };
2027  /* These delims act as neutral breaks. Whether the url is ended
2028  or not depends on the next non-neutral character. We'll test
2029  with Y unchanged, in which case the url should include the
2030  deliminator and the Y. We'll also test with the Y changed
2031  to a space, in which case the url stops before the
2032  deliminator. */
2033  const char * templates_neutral_delim[] = {
2034  "This is some text with X-Y on it",
2035  "This is some text with X--Y on it",
2036  "This is some text with X!Y on it",
2037  "This is some text with X[Y on it",
2038  "This is some text with X]Y on it",
2039  "This is some text with X{Y on it",
2040  "This is some text with X}Y on it",
2041  "This is some text with X(Y on it",
2042  "This is some text with X)Y on it",
2043  "This is some text with X\"Y on it",
2044  "This is some text with X;Y on it",
2045  "This is some text with X:Y on it",
2046  "This is some text with X'Y on it",
2047  "This is some text with X?Y on it",
2048  "This is some text with X<Y on it",
2049  "This is some text with X>Y on it",
2050  "This is some text with X.Y on it",
2051  "This is some text with X,Y on it",
2052  };
2053  char buffer[1024];
2054 
2056  hwndRichEdit = new_richedit(parent);
2057  /* Try and pass EM_AUTOURLDETECT some test wParam values */
2058  urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2059  ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2060  urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2061  ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2062  /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2063  urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2064  ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2065  urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2066  ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2067  /* for each url, check the text to see if CFE_LINK effect is present */
2068  for (i = 0; i < ARRAY_SIZE(urls); i++) {
2069 
2070  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2071  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2072  check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2073 
2074  /* Link detection should happen immediately upon WM_SETTEXT */
2075  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2076  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2077  check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2078  }
2079  DestroyWindow(hwndRichEdit);
2080 
2081  /* Test detection of URLs within normal text - WM_SETTEXT case. */
2082  for (i = 0; i < ARRAY_SIZE(urls); i++) {
2083  hwndRichEdit = new_richedit(parent);
2084 
2085  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2086  char * at_pos;
2087  int at_offset;
2088  int end_offset;
2089 
2090  at_pos = strchr(templates_delim[j], 'X');
2091  at_offset = at_pos - templates_delim[j];
2092  memcpy(buffer, templates_delim[j], at_offset);
2093  buffer[at_offset] = '\0';
2094  strcat(buffer, urls[i].text);
2095  strcat(buffer, templates_delim[j] + at_offset + 1);
2096  end_offset = at_offset + strlen(urls[i].text);
2097 
2098  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2099  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2100 
2101  /* This assumes no templates start with the URL itself, and that they
2102  have at least two characters before the URL text */
2103  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2104  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2105  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2106  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2107  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2108  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2109 
2110  if (urls[i].is_url)
2111  {
2112  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2114  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2116  }
2117  else
2118  {
2119  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2120  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2121  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2122  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2123  }
2124  if (buffer[end_offset] != '\0')
2125  {
2126  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2127  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2128  if (buffer[end_offset +1] != '\0')
2129  {
2130  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2131  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2132  }
2133  }
2134  }
2135 
2136  for (j = 0; j < ARRAY_SIZE(templates_non_delim); j++) {
2137  char * at_pos;
2138  int at_offset;
2139  int end_offset;
2140 
2141  at_pos = strchr(templates_non_delim[j], 'X');
2142  at_offset = at_pos - templates_non_delim[j];
2143  memcpy(buffer, templates_non_delim[j], at_offset);
2144  buffer[at_offset] = '\0';
2145  strcat(buffer, urls[i].text);
2146  strcat(buffer, templates_non_delim[j] + at_offset + 1);
2147  end_offset = at_offset + strlen(urls[i].text);
2148 
2149  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2150  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2151 
2152  /* This assumes no templates start with the URL itself, and that they
2153  have at least two characters before the URL text */
2154  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2155  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2156  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2157  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2158  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2159  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2160 
2161  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2163  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165  if (buffer[end_offset] != '\0')
2166  {
2167  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2168  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2169  if (buffer[end_offset +1] != '\0')
2170  {
2171  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2172  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2173  }
2174  }
2175  }
2176 
2177  for (j = 0; j < ARRAY_SIZE(templates_xten_delim); j++) {
2178  char * at_pos;
2179  int at_offset;
2180  int end_offset;
2181 
2182  at_pos = strchr(templates_xten_delim[j], 'X');
2183  at_offset = at_pos - templates_xten_delim[j];
2184  memcpy(buffer, templates_xten_delim[j], at_offset);
2185  buffer[at_offset] = '\0';
2186  strcat(buffer, urls[i].text);
2187  strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2188  end_offset = at_offset + strlen(urls[i].text);
2189 
2190  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2191  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2192 
2193  /* This assumes no templates start with the URL itself, and that they
2194  have at least two characters before the URL text */
2195  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2196  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2197  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2198  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2199  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2200  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2201 
2202  if (urls[i].is_url)
2203  {
2204  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2206  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2208  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2209  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2210  }
2211  else
2212  {
2213  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2214  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2215  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2216  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2217  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2218  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2219  }
2220  if (buffer[end_offset +1] != '\0')
2221  {
2222  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2223  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2224  if (buffer[end_offset +2] != '\0')
2225  {
2226  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2227  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2228  }
2229  }
2230  }
2231 
2232  for (j = 0; j < ARRAY_SIZE(templates_neutral_delim); j++) {
2233  char * at_pos, * end_pos;
2234  int at_offset;
2235  int end_offset;
2236 
2237  if (!urls[i].is_url) continue;
2238 
2239  at_pos = strchr(templates_neutral_delim[j], 'X');
2240  at_offset = at_pos - templates_neutral_delim[j];
2241  memcpy(buffer, templates_neutral_delim[j], at_offset);
2242  buffer[at_offset] = '\0';
2243  strcat(buffer, urls[i].text);
2244  strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2245 
2246  end_pos = strchr(buffer, 'Y');
2247  end_offset = end_pos - buffer;
2248 
2249  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2250  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2251 
2252  /* This assumes no templates start with the URL itself, and that they
2253  have at least two characters before the URL text */
2254  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2255  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2256  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2257  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2258  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2259  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2260 
2261  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2262  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2263  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2264  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2265  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2266  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2267 
2268  *end_pos = ' ';
2269 
2270  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2271  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2272 
2273  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2274  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2275  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2276  "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2277  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2278  "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2279  }
2280 
2281  DestroyWindow(hwndRichEdit);
2282  hwndRichEdit = NULL;
2283  }
2284 
2285  /* Test detection of URLs within normal text - WM_CHAR case. */
2286  /* Test only the first two URL examples for brevity */
2287  for (i = 0; i < 2; i++) {
2288  hwndRichEdit = new_richedit(parent);
2289 
2290  /* Also for brevity, test only the first three delimiters */
2291  for (j = 0; j < 3; j++) {
2292  char * at_pos;
2293  int at_offset;
2294  int end_offset;
2295  int u, v;
2296 
2297  at_pos = strchr(templates_delim[j], 'X');
2298  at_offset = at_pos - templates_delim[j];
2299  end_offset = at_offset + strlen(urls[i].text);
2300 
2301  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2302  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2303  for (u = 0; templates_delim[j][u]; u++) {
2304  if (templates_delim[j][u] == '\r') {
2305  simulate_typing_characters(hwndRichEdit, "\r");
2306  } else if (templates_delim[j][u] != 'X') {
2307  SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2308  } else {
2309  for (v = 0; urls[i].text[v]; v++) {
2310  SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2311  }
2312  }
2313  }
2314  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2315 
2316  /* This assumes no templates start with the URL itself, and that they
2317  have at least two characters before the URL text */
2318  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2319  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2320  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2321  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2322  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2323  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2324 
2325  if (urls[i].is_url)
2326  {
2327  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2328  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2329  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2330  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2331  }
2332  else
2333  {
2334  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2336  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2338  }
2339  if (buffer[end_offset] != '\0')
2340  {
2341  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2342  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2343  if (buffer[end_offset +1] != '\0')
2344  {
2345  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2346  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2347  }
2348  }
2349 
2350  /* The following will insert a paragraph break after the first character
2351  of the URL candidate, thus breaking the URL. It is expected that the
2352  CFE_LINK attribute should break across both pieces of the URL */
2353  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2354  simulate_typing_characters(hwndRichEdit, "\r");
2355  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2356 
2357  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2358  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2359  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2360  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2361  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2362  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2363 
2364  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2365  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2366  /* end_offset moved because of paragraph break */
2367  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2368  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2369  ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2370  if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2371  {
2372  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2373  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2374  if (buffer[end_offset +2] != '\0')
2375  {
2376  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2377  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2378  }
2379  }
2380 
2381  /* The following will remove the just-inserted paragraph break, thus
2382  restoring the URL */
2383  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2384  simulate_typing_characters(hwndRichEdit, "\b");
2385  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2386 
2387  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2388  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2389  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2390  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2391  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2392  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2393 
2394  if (urls[i].is_url)
2395  {
2396  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2397  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2398  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2399  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2400  }
2401  else
2402  {
2403  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2404  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2405  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2406  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2407  }
2408  if (buffer[end_offset] != '\0')
2409  {
2410  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2411  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2412  if (buffer[end_offset +1] != '\0')
2413  {
2414  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2415  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2416  }
2417  }
2418  }
2419  DestroyWindow(hwndRichEdit);
2420  hwndRichEdit = NULL;
2421  }
2422 
2423  /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2424  /* Test just the first two URL examples for brevity */
2425  for (i = 0; i < 2; i++) {
2426  SETTEXTEX st;
2427 
2428  hwndRichEdit = new_richedit(parent);
2429 
2430  /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2431  be detected:
2432  1) Set entire text, a la WM_SETTEXT
2433  2) Set a selection of the text to the URL
2434  3) Set a portion of the text at a time, which eventually results in
2435  an URL
2436  All of them should give equivalent results
2437  */
2438 
2439  /* Set entire text in one go, like WM_SETTEXT */
2440  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2441  char * at_pos;
2442  int at_offset;
2443  int end_offset;
2444 
2445  st.codepage = CP_ACP;
2446  st.flags = ST_DEFAULT;
2447 
2448  at_pos = strchr(templates_delim[j], 'X');
2449  at_offset = at_pos - templates_delim[j];
2450  memcpy(buffer, templates_delim[j], at_offset);
2451  buffer[at_offset] = '\0';
2452  strcat(buffer, urls[i].text);
2453  strcat(buffer, templates_delim[j] + at_offset + 1);
2454  end_offset = at_offset + strlen(urls[i].text);
2455 
2456  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2457  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2458 
2459  /* This assumes no templates start with the URL itself, and that they
2460  have at least two characters before the URL text */
2461  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2462  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2463  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2464  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2465  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2466  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2467 
2468  if (urls[i].is_url)
2469  {
2470  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2471  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2472  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2473  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2474  }
2475  else
2476  {
2477  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2478  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2479  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2480  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2481  }
2482  if (buffer[end_offset] != '\0')
2483  {
2484  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2485  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2486  if (buffer[end_offset +1] != '\0')
2487  {
2488  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2489  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2490  }
2491  }
2492  }
2493 
2494  /* Set selection with X to the URL */
2495  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2496  char * at_pos;
2497  int at_offset;
2498  int end_offset;
2499 
2500  at_pos = strchr(templates_delim[j], 'X');
2501  at_offset = at_pos - templates_delim[j];
2502  end_offset = at_offset + strlen(urls[i].text);
2503 
2504  st.codepage = CP_ACP;
2505  st.flags = ST_DEFAULT;
2506  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2507  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2508  st.flags = ST_SELECTION;
2509  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2510  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2511  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2512 
2513  /* This assumes no templates start with the URL itself, and that they
2514  have at least two characters before the URL text */
2515  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2516  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2517  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2518  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2519  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2520  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2521 
2522  if (urls[i].is_url)
2523  {
2524  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2525  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2526  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2527  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2528  }
2529  else
2530  {
2531  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2532  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2533  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2534  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2535  }
2536  if (buffer[end_offset] != '\0')
2537  {
2538  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2539  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2540  if (buffer[end_offset +1] != '\0')
2541  {
2542  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2543  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2544  }
2545  }
2546  }
2547 
2548  /* Set selection with X to the first character of the URL, then the rest */
2549  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2550  char * at_pos;
2551  int at_offset;
2552  int end_offset;
2553 
2554  at_pos = strchr(templates_delim[j], 'X');
2555  at_offset = at_pos - templates_delim[j];
2556  end_offset = at_offset + strlen(urls[i].text);
2557 
2558  strcpy(buffer, "YY");
2559  buffer[0] = urls[i].text[0];
2560 
2561  st.codepage = CP_ACP;
2562  st.flags = ST_DEFAULT;
2563  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2564  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2565  st.flags = ST_SELECTION;
2566  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2567  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2568  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2569  SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2570  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2571 
2572  /* This assumes no templates start with the URL itself, and that they
2573  have at least two characters before the URL text */
2574  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2575  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2576  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2577  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2578  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2579  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2580 
2581  if (urls[i].is_url)
2582  {
2583  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2584  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2585  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2586  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2587  }
2588  else
2589  {
2590  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2591  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2592  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2593  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2594  }
2595  if (buffer[end_offset] != '\0')
2596  {
2597  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2598  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2599  if (buffer[end_offset +1] != '\0')
2600  {
2601  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2602  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2603  }
2604  }
2605  }
2606 
2607  DestroyWindow(hwndRichEdit);
2608  hwndRichEdit = NULL;
2609  }
2610 
2611  /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2612  /* Test just the first two URL examples for brevity */
2613  for (i = 0; i < 2; i++) {
2614  hwndRichEdit = new_richedit(parent);
2615 
2616  /* Set selection with X to the URL */
2617  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2618  char * at_pos;
2619  int at_offset;
2620  int end_offset;
2621 
2622  at_pos = strchr(templates_delim[j], 'X');
2623  at_offset = at_pos - templates_delim[j];
2624  end_offset = at_offset + strlen(urls[i].text);
2625 
2626  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2627  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2628  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2629  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2630  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2631 
2632  /* This assumes no templates start with the URL itself, and that they
2633  have at least two characters before the URL text */
2634  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2635  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2636  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2637  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2638  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2639  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2640 
2641  if (urls[i].is_url)
2642  {
2643  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2644  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2645  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2646  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2647  }
2648  else
2649  {
2650  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2651  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2652  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2653  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2654  }
2655  if (buffer[end_offset] != '\0')
2656  {
2657  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2658  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2659  if (buffer[end_offset +1] != '\0')
2660  {
2661  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2662  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2663  }
2664  }
2665  }
2666 
2667  /* Set selection with X to the first character of the URL, then the rest */
2668  for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2669  char * at_pos;
2670  int at_offset;
2671  int end_offset;
2672 
2673  at_pos = strchr(templates_delim[j], 'X');
2674  at_offset = at_pos - templates_delim[j];
2675  end_offset = at_offset + strlen(urls[i].text);
2676 
2677  strcpy(buffer, "YY");
2678  buffer[0] = urls[i].text[0];
2679 
2680  SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2681  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2682  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2683  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2684  SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2685  SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2686  SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2687 
2688  /* This assumes no templates start with the URL itself, and that they
2689  have at least two characters before the URL text */
2690  ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2691  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2692  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2693  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2694  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2695  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2696 
2697  if (urls[i].is_url)
2698  {
2699  ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2700  "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2701  ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2702  "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2703  }
2704  else
2705  {
2706  ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2707  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2708  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2709  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2710  }
2711  if (buffer[end_offset] != '\0')
2712  {
2713  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2714  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2715  if (buffer[end_offset +1] != '\0')
2716  {
2717  ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2718  "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2719  }
2720  }
2721  }
2722 
2723  DestroyWindow(hwndRichEdit);
2724  hwndRichEdit = NULL;
2725  }
2726 
2728 }
2729 
2730 static void test_EM_SCROLL(void)
2731 {
2732  int i, j;
2733  int r; /* return value */
2734  int expr; /* expected return value */
2735  HWND hwndRichEdit = new_richedit(NULL);
2736  int y_before, y_after; /* units of lines of text */
2737 
2738  /* test a richedit box containing a single line of text */
2739  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2740  expr = 0x00010000;
2741  for (i = 0; i < 4; i++) {
2742  static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2743 
2744  r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2745  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2746  ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2747  "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2748  ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2749  "(i == %d)\n", y_after, i);
2750  }
2751 
2752  /*
2753  * test a richedit box that will scroll. There are two general
2754  * cases: the case without any long lines and the case with a long
2755  * line.
2756  */
2757  for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2758  if (i == 0)
2759  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2760  else
2761  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2762  "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2763  "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2764  "LONG LINE \nb\nc\nd\ne");
2765  for (j = 0; j < 12; j++) /* reset scroll position to top */
2766  SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2767 
2768  /* get first visible line */
2769  y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2770  r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2771 
2772  /* get new current first visible line */
2773  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2774 
2775  ok(((r & 0xffffff00) == 0x00010000) &&
2776  ((r & 0x000000ff) != 0x00000000),
2777  "EM_SCROLL page down didn't scroll by a small positive number of "
2778  "lines (r == 0x%08x)\n", r);
2779  ok(y_after > y_before, "EM_SCROLL page down not functioning "
2780  "(line %d scrolled to line %d\n", y_before, y_after);
2781 
2782  y_before = y_after;
2783 
2784  r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2785  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2786  ok(((r & 0xffffff00) == 0x0001ff00),
2787  "EM_SCROLL page up didn't scroll by a small negative number of lines "
2788  "(r == 0x%08x)\n", r);
2789  ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2790  "%d scrolled to line %d\n", y_before, y_after);
2791 
2792  y_before = y_after;
2793 
2794  r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2795 
2796  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2797 
2798  ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2799  "(r == 0x%08x)\n", r);
2800  ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2801  "1 line (%d scrolled to %d)\n", y_before, y_after);
2802 
2803  y_before = y_after;
2804 
2805  r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2806 
2807  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2808 
2809  ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2810  "(r == 0x%08x)\n", r);
2811  ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2812  "line (%d scrolled to %d)\n", y_before, y_after);
2813 
2814  y_before = y_after;
2815 
2816  r = SendMessageA(hwndRichEdit, EM_SCROLL,
2817  SB_LINEUP, 0); /* lineup beyond top */
2818 
2819  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2820 
2821  ok(r == 0x00010000,
2822  "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2823  ok(y_before == y_after,
2824  "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2825 
2826  y_before = y_after;
2827 
2828  r = SendMessageA(hwndRichEdit, EM_SCROLL,
2829  SB_PAGEUP, 0);/*page up beyond top */
2830 
2831  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2832 
2833  ok(r == 0x00010000,
2834  "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2835  ok(y_before == y_after,
2836  "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2837 
2838  for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2839  SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2840  y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2841  r = SendMessageA(hwndRichEdit, EM_SCROLL,
2842  SB_PAGEDOWN, 0); /* page down beyond bot */
2843  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2844 
2845  ok(r == 0x00010000,
2846  "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2847  ok(y_before == y_after,
2848  "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2849  y_before, y_after);
2850 
2851  y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2852  r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2853  y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2854 
2855  ok(r == 0x00010000,
2856  "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2857  ok(y_before == y_after,
2858  "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2859  y_before, y_after);
2860  }
2861  DestroyWindow(hwndRichEdit);
2862 }
2863 
2864 static unsigned int recursionLevel = 0;
2865 static unsigned int WM_SIZE_recursionLevel = 0;
2868 
2870 {
2871  LRESULT r;
2872 
2873  if (bailedOutOfRecursion) return 0;
2874  if (recursionLevel >= 32) {
2876  return 0;
2877  }
2878 
2879  recursionLevel++;
2880  switch (message) {
2881  case WM_SIZE:
2883  r = richeditProc(hwnd, message, wParam, lParam);
2884  /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2887  break;
2888  default:
2889  r = richeditProc(hwnd, message, wParam, lParam);
2890  break;
2891  }
2892  recursionLevel--;
2893  return r;
2894 }
2895 
2896 static void test_scrollbar_visibility(void)
2897 {
2898  HWND hwndRichEdit;
2899  const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2900  SCROLLINFO si;
2901  WNDCLASSA cls;
2902  BOOL r;
2903 
2904  /* These tests show that richedit should temporarily refrain from automatically
2905  hiding or showing its scrollbars (vertical at least) when an explicit request
2906  is made via ShowScrollBar() or similar, outside of standard richedit logic.
2907  Some applications depend on forced showing (when otherwise richedit would
2908  hide the vertical scrollbar) and are thrown on an endless recursive loop
2909  if richedit auto-hides the scrollbar again. Apparently they never heard of
2910  the ES_DISABLENOSCROLL style... */
2911 
2912  hwndRichEdit = new_richedit(NULL);
2913 
2914  /* Test default scrollbar visibility behavior */
2915  memset(&si, 0, sizeof(si));
2916  si.cbSize = sizeof(si);
2917  si.fMask = SIF_PAGE | SIF_RANGE;
2918  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2920  "Vertical scrollbar is visible, should be invisible.\n");
2921  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923  si.nPage, si.nMin, si.nMax);
2924 
2925  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2926  memset(&si, 0, sizeof(si));
2927  si.cbSize = sizeof(si);
2928  si.fMask = SIF_PAGE | SIF_RANGE;
2929  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931  "Vertical scrollbar is visible, should be invisible.\n");
2932  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934  si.nPage, si.nMin, si.nMax);
2935 
2936  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2937  memset(&si, 0, sizeof(si));
2938  si.cbSize = sizeof(si);
2939  si.fMask = SIF_PAGE | SIF_RANGE;
2940  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2942  "Vertical scrollbar is invisible, should be visible.\n");
2943  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2944  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2945  si.nPage, si.nMin, si.nMax);
2946 
2947  /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2948  even though it hides the scrollbar */
2949  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950  memset(&si, 0, sizeof(si));
2951  si.cbSize = sizeof(si);
2952  si.fMask = SIF_PAGE | SIF_RANGE;
2953  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955  "Vertical scrollbar is visible, should be invisible.\n");
2956  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958  si.nPage, si.nMin, si.nMax);
2959 
2960  /* Setting non-scrolling text again does *not* reset scrollbar range */
2961  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2962  memset(&si, 0, sizeof(si));
2963  si.cbSize = sizeof(si);
2964  si.fMask = SIF_PAGE | SIF_RANGE;
2965  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2966  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2967  "Vertical scrollbar is visible, should be invisible.\n");
2968  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2969  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2970  si.nPage, si.nMin, si.nMax);
2971 
2972  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2973  memset(&si, 0, sizeof(si));
2974  si.cbSize = sizeof(si);
2975  si.fMask = SIF_PAGE | SIF_RANGE;
2976  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2977  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2978  "Vertical scrollbar is visible, should be invisible.\n");
2979  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2980  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2981  si.nPage, si.nMin, si.nMax);
2982 
2983  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2984  memset(&si, 0, sizeof(si));
2985  si.cbSize = sizeof(si);
2986  si.fMask = SIF_PAGE | SIF_RANGE;
2987  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2988  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2989  "Vertical scrollbar is visible, should be invisible.\n");
2990  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2991  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2992  si.nPage, si.nMin, si.nMax);
2993 
2994  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2995  memset(&si, 0, sizeof(si));
2996  si.cbSize = sizeof(si);
2997  si.fMask = SIF_PAGE | SIF_RANGE;
2998  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2999  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3000  "Vertical scrollbar is visible, should be invisible.\n");
3001  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3002  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3003  si.nPage, si.nMin, si.nMax);
3004 
3005  DestroyWindow(hwndRichEdit);
3006 
3007  /* Test again, with ES_DISABLENOSCROLL style */
3009 
3010  /* Test default scrollbar visibility behavior */
3011  memset(&si, 0, sizeof(si));
3012  si.cbSize = sizeof(si);
3013  si.fMask = SIF_PAGE | SIF_RANGE;
3014  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016  "Vertical scrollbar is invisible, should be visible.\n");
3017  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3018  "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3019  si.nPage, si.nMin, si.nMax);
3020 
3021  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3022  memset(&si, 0, sizeof(si));
3023  si.cbSize = sizeof(si);
3024  si.fMask = SIF_PAGE | SIF_RANGE;
3025  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3027  "Vertical scrollbar is invisible, should be visible.\n");
3028  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3029  "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3030  si.nPage, si.nMin, si.nMax);
3031 
3032  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3033  memset(&si, 0, sizeof(si));
3034  si.cbSize = sizeof(si);
3035  si.fMask = SIF_PAGE | SIF_RANGE;
3036  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3038  "Vertical scrollbar is invisible, should be visible.\n");
3039  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3040  "reported page/range is %d (%d..%d)\n",
3041  si.nPage, si.nMin, si.nMax);
3042 
3043  /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3044  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3045  memset(&si, 0, sizeof(si));
3046  si.cbSize = sizeof(si);
3047  si.fMask = SIF_PAGE | SIF_RANGE;
3048  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050  "Vertical scrollbar is invisible, should be visible.\n");
3051  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3052  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3053  si.nPage, si.nMin, si.nMax);
3054 
3055  /* Setting non-scrolling text again does *not* reset scrollbar range */
3056  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3057  memset(&si, 0, sizeof(si));
3058  si.cbSize = sizeof(si);
3059  si.fMask = SIF_PAGE | SIF_RANGE;
3060  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3061  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3062  "Vertical scrollbar is invisible, should be visible.\n");
3063  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3064  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3065  si.nPage, si.nMin, si.nMax);
3066 
3067  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3068  memset(&si, 0, sizeof(si));
3069  si.cbSize = sizeof(si);
3070  si.fMask = SIF_PAGE | SIF_RANGE;
3071  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3072  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3073  "Vertical scrollbar is invisible, should be visible.\n");
3074  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3075  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3076  si.nPage, si.nMin, si.nMax);
3077 
3078  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3079  memset(&si, 0, sizeof(si));
3080  si.cbSize = sizeof(si);
3081  si.fMask = SIF_PAGE | SIF_RANGE;
3082  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084  "Vertical scrollbar is invisible, should be visible.\n");
3085  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3086  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3087  si.nPage, si.nMin, si.nMax);
3088 
3089  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3090  memset(&si, 0, sizeof(si));
3091  si.cbSize = sizeof(si);
3092  si.fMask = SIF_PAGE | SIF_RANGE;
3093  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3094  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3095  "Vertical scrollbar is invisible, should be visible.\n");
3096  ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3097  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3098  si.nPage, si.nMin, si.nMax);
3099 
3100  DestroyWindow(hwndRichEdit);
3101 
3102  /* Test behavior with explicit visibility request, using ShowScrollBar() */
3103  hwndRichEdit = new_richedit(NULL);
3104 
3105  /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3106  ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3107  memset(&si, 0, sizeof(si));
3108  si.cbSize = sizeof(si);
3109  si.fMask = SIF_PAGE | SIF_RANGE;
3110  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3112  "Vertical scrollbar is invisible, should be visible.\n");
3113  todo_wine {
3114  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3115  "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3116  si.nPage, si.nMin, si.nMax);
3117  }
3118 
3119  /* Ditto, see above */
3120  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3121  memset(&si, 0, sizeof(si));
3122  si.cbSize = sizeof(si);
3123  si.fMask = SIF_PAGE | SIF_RANGE;
3124  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126  "Vertical scrollbar is invisible, should be visible.\n");
3127  todo_wine {
3128  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3129  "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3130  si.nPage, si.nMin, si.nMax);
3131  }
3132 
3133  /* Ditto, see above */
3134  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3135  memset(&si, 0, sizeof(si));
3136  si.cbSize = sizeof(si);
3137  si.fMask = SIF_PAGE | SIF_RANGE;
3138  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3139  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3140  "Vertical scrollbar is invisible, should be visible.\n");
3141  todo_wine {
3142  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3143  "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3144  si.nPage, si.nMin, si.nMax);
3145  }
3146 
3147  /* Ditto, see above */
3148  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3149  memset(&si, 0, sizeof(si));
3150  si.cbSize = sizeof(si);
3151  si.fMask = SIF_PAGE | SIF_RANGE;
3152  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3153  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3154  "Vertical scrollbar is invisible, should be visible.\n");
3155  todo_wine {
3156  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3157  "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3158  si.nPage, si.nMin, si.nMax);
3159  }
3160 
3161  /* Ditto, see above */
3162  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3163  memset(&si, 0, sizeof(si));
3164  si.cbSize = sizeof(si);
3165  si.fMask = SIF_PAGE | SIF_RANGE;
3166  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3168  "Vertical scrollbar is invisible, should be visible.\n");
3169  todo_wine {
3170  ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3171  "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3172  si.nPage, si.nMin, si.nMax);
3173  }
3174 
3175  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3176  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3177  memset(&si, 0, sizeof(si));
3178  si.cbSize = sizeof(si);
3179  si.fMask = SIF_PAGE | SIF_RANGE;
3180  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3181  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3182  "Vertical scrollbar is visible, should be invisible.\n");
3183  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3184  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3185  si.nPage, si.nMin, si.nMax);
3186 
3187  DestroyWindow(hwndRichEdit);
3188 
3189  hwndRichEdit = new_richedit(NULL);
3190 
3191  ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3192  memset(&si, 0, sizeof(si));
3193  si.cbSize = sizeof(si);
3194  si.fMask = SIF_PAGE | SIF_RANGE;
3195  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3196  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3197  "Vertical scrollbar is visible, should be invisible.\n");
3198  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3199  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3200  si.nPage, si.nMin, si.nMax);
3201 
3202  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3203  memset(&si, 0, sizeof(si));
3204  si.cbSize = sizeof(si);
3205  si.fMask = SIF_PAGE | SIF_RANGE;
3206  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3207  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3208  "Vertical scrollbar is visible, should be invisible.\n");
3209  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3210  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3211  si.nPage, si.nMin, si.nMax);
3212 
3213  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3214  memset(&si, 0, sizeof(si));
3215  si.cbSize = sizeof(si);
3216  si.fMask = SIF_PAGE | SIF_RANGE;
3217  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3218  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3219  "Vertical scrollbar is visible, should be invisible.\n");
3220  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3221  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3222  si.nPage, si.nMin, si.nMax);
3223 
3224  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3225  memset(&si, 0, sizeof(si));
3226  si.cbSize = sizeof(si);
3227  si.fMask = SIF_PAGE | SIF_RANGE;
3228  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3229  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3230  "Vertical scrollbar is visible, should be invisible.\n");
3231  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3232  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3233  si.nPage, si.nMin, si.nMax);
3234 
3235  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3236  memset(&si, 0, sizeof(si));
3237  si.cbSize = sizeof(si);
3238  si.fMask = SIF_PAGE | SIF_RANGE;
3239  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3240  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3241  "Vertical scrollbar is invisible, should be visible.\n");
3242  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3243  "reported page/range is %d (%d..%d)\n",
3244  si.nPage, si.nMin, si.nMax);
3245 
3246  /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3247  ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3248  memset(&si, 0, sizeof(si));
3249  si.cbSize = sizeof(si);
3250  si.fMask = SIF_PAGE | SIF_RANGE;
3251  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3252  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3253  "Vertical scrollbar is visible, should be invisible.\n");
3254  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3255  "reported page/range is %d (%d..%d)\n",
3256  si.nPage, si.nMin, si.nMax);
3257 
3258  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3259  memset(&si, 0, sizeof(si));
3260  si.cbSize = sizeof(si);
3261  si.fMask = SIF_PAGE | SIF_RANGE;
3262  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3263  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3264  "Vertical scrollbar is visible, should be invisible.\n");
3265  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3266  "reported page/range is %d (%d..%d)\n",
3267  si.nPage, si.nMin, si.nMax);
3268 
3269  /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3270  EM_SCROLL will make visible any forcefully invisible scrollbar */
3271  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3272  memset(&si, 0, sizeof(si));
3273  si.cbSize = sizeof(si);
3274  si.fMask = SIF_PAGE | SIF_RANGE;
3275  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3276  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3277  "Vertical scrollbar is invisible, should be visible.\n");
3278  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3279  "reported page/range is %d (%d..%d)\n",
3280  si.nPage, si.nMin, si.nMax);
3281 
3282  ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3283  memset(&si, 0, sizeof(si));
3284  si.cbSize = sizeof(si);
3285  si.fMask = SIF_PAGE | SIF_RANGE;
3286  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3287  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3288  "Vertical scrollbar is visible, should be invisible.\n");
3289  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3290  "reported page/range is %d (%d..%d)\n",
3291  si.nPage, si.nMin, si.nMax);
3292 
3293  /* Again, EM_SCROLL, with SB_LINEUP */
3294  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3295  memset(&si, 0, sizeof(si));
3296  si.cbSize = sizeof(si);
3297  si.fMask = SIF_PAGE | SIF_RANGE;
3298  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3299  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3300  "Vertical scrollbar is invisible, should be visible.\n");
3301  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3302  "reported page/range is %d (%d..%d)\n",
3303  si.nPage, si.nMin, si.nMax);
3304 
3305  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3306  memset(&si, 0, sizeof(si));
3307  si.cbSize = sizeof(si);
3308  si.fMask = SIF_PAGE | SIF_RANGE;
3309  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3310  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3311  "Vertical scrollbar is visible, should be invisible.\n");
3312  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3313  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3314  si.nPage, si.nMin, si.nMax);
3315 
3316  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3317  memset(&si, 0, sizeof(si));
3318  si.cbSize = sizeof(si);
3319  si.fMask = SIF_PAGE | SIF_RANGE;
3320  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3321  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3322  "Vertical scrollbar is invisible, should be visible.\n");
3323  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3324  "reported page/range is %d (%d..%d)\n",
3325  si.nPage, si.nMin, si.nMax);
3326 
3327  DestroyWindow(hwndRichEdit);
3328 
3329 
3330  /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3331  hwndRichEdit = new_richedit(NULL);
3332 
3333 #define ENABLE_WS_VSCROLL(hwnd) \
3334  SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3335 #define DISABLE_WS_VSCROLL(hwnd) \
3336  SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3337 
3338  /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3339  ENABLE_WS_VSCROLL(hwndRichEdit);
3340  memset(&si, 0, sizeof(si));
3341  si.cbSize = sizeof(si);
3342  si.fMask = SIF_PAGE | SIF_RANGE;
3343  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3344  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3345  "Vertical scrollbar is invisible, should be visible.\n");
3346  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3347  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3348  si.nPage, si.nMin, si.nMax);
3349 
3350  /* Ditto, see above */
3351  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3352  memset(&si, 0, sizeof(si));
3353  si.cbSize = sizeof(si);
3354  si.fMask = SIF_PAGE | SIF_RANGE;
3355  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3356  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3357  "Vertical scrollbar is invisible, should be visible.\n");
3358  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3359  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3360  si.nPage, si.nMin, si.nMax);
3361 
3362  /* Ditto, see above */
3363  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3364  memset(&si, 0, sizeof(si));
3365  si.cbSize = sizeof(si);
3366  si.fMask = SIF_PAGE | SIF_RANGE;
3367  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3368  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3369  "Vertical scrollbar is invisible, should be visible.\n");
3370  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3371  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3372  si.nPage, si.nMin, si.nMax);
3373 
3374  /* Ditto, see above */
3375  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3376  memset(&si, 0, sizeof(si));
3377  si.cbSize = sizeof(si);
3378  si.fMask = SIF_PAGE | SIF_RANGE;
3379  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3380  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3381  "Vertical scrollbar is invisible, should be visible.\n");
3382  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3383  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3384  si.nPage, si.nMin, si.nMax);
3385 
3386  /* Ditto, see above */
3387  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3388  memset(&si, 0, sizeof(si));
3389  si.cbSize = sizeof(si);
3390  si.fMask = SIF_PAGE | SIF_RANGE;
3391  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3392  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3393  "Vertical scrollbar is invisible, should be visible.\n");
3394  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3395  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3396  si.nPage, si.nMin, si.nMax);
3397 
3398  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3399  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3400  memset(&si, 0, sizeof(si));
3401  si.cbSize = sizeof(si);
3402  si.fMask = SIF_PAGE | SIF_RANGE;
3403  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3404  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3405  "Vertical scrollbar is visible, should be invisible.\n");
3406  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3407  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3408  si.nPage, si.nMin, si.nMax);
3409 
3410  DestroyWindow(hwndRichEdit);
3411 
3412  hwndRichEdit = new_richedit(NULL);
3413 
3414  DISABLE_WS_VSCROLL(hwndRichEdit);
3415  memset(&si, 0, sizeof(si));
3416  si.cbSize = sizeof(si);
3417  si.fMask = SIF_PAGE | SIF_RANGE;
3418  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3419  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3420  "Vertical scrollbar is visible, should be invisible.\n");
3421  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3422  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3423  si.nPage, si.nMin, si.nMax);
3424 
3425  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3426  memset(&si, 0, sizeof(si));
3427  si.cbSize = sizeof(si);
3428  si.fMask = SIF_PAGE | SIF_RANGE;
3429  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3430  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3431  "Vertical scrollbar is visible, should be invisible.\n");
3432  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3433  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3434  si.nPage, si.nMin, si.nMax);
3435 
3436  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3437  memset(&si, 0, sizeof(si));
3438  si.cbSize = sizeof(si);
3439  si.fMask = SIF_PAGE | SIF_RANGE;
3440  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3441  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3442  "Vertical scrollbar is visible, should be invisible.\n");
3443  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3444  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3445  si.nPage, si.nMin, si.nMax);
3446 
3447  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3448  memset(&si, 0, sizeof(si));
3449  si.cbSize = sizeof(si);
3450  si.fMask = SIF_PAGE | SIF_RANGE;
3451  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3452  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3453  "Vertical scrollbar is visible, should be invisible.\n");
3454  ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3455  "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3456  si.nPage, si.nMin, si.nMax);
3457 
3458  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3459  memset(&si, 0, sizeof(si));
3460  si.cbSize = sizeof(si);
3461  si.fMask = SIF_PAGE | SIF_RANGE;
3462  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3463  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3464  "Vertical scrollbar is invisible, should be visible.\n");
3465  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3466  "reported page/range is %d (%d..%d)\n",
3467  si.nPage, si.nMin, si.nMax);
3468 
3469  /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3470  DISABLE_WS_VSCROLL(hwndRichEdit);
3471  memset(&si, 0, sizeof(si));
3472  si.cbSize = sizeof(si);
3473  si.fMask = SIF_PAGE | SIF_RANGE;
3474  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3475  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3476  "Vertical scrollbar is visible, should be invisible.\n");
3477  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3478  "reported page/range is %d (%d..%d)\n",
3479  si.nPage, si.nMin, si.nMax);
3480 
3481  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3482  memset(&si, 0, sizeof(si));
3483  si.cbSize = sizeof(si);
3484  si.fMask = SIF_PAGE | SIF_RANGE;
3485  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3486  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3487  "Vertical scrollbar is visible, should be invisible.\n");
3488  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3489  "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3490  si.nPage, si.nMin, si.nMax);
3491 
3492  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3493  memset(&si, 0, sizeof(si));
3494  si.cbSize = sizeof(si);
3495  si.fMask = SIF_PAGE | SIF_RANGE;
3496  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3497  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3498  "Vertical scrollbar is invisible, should be visible.\n");
3499  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3500  "reported page/range is %d (%d..%d)\n",
3501  si.nPage, si.nMin, si.nMax);
3502 
3503  DISABLE_WS_VSCROLL(hwndRichEdit);
3504  memset(&si, 0, sizeof(si));
3505  si.cbSize = sizeof(si);
3506  si.fMask = SIF_PAGE | SIF_RANGE;
3507  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3508  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3509  "Vertical scrollbar is visible, should be invisible.\n");
3510  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3511  "reported page/range is %d (%d..%d)\n",
3512  si.nPage, si.nMin, si.nMax);
3513 
3514  /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3515  EM_SCROLL will make visible any forcefully invisible scrollbar */
3516  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3517  memset(&si, 0, sizeof(si));
3518  si.cbSize = sizeof(si);
3519  si.fMask = SIF_PAGE | SIF_RANGE;
3520  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3521  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3522  "Vertical scrollbar is invisible, should be visible.\n");
3523  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3524  "reported page/range is %d (%d..%d)\n",
3525  si.nPage, si.nMin, si.nMax);
3526 
3527  DISABLE_WS_VSCROLL(hwndRichEdit);
3528  memset(&si, 0, sizeof(si));
3529  si.cbSize = sizeof(si);
3530  si.fMask = SIF_PAGE | SIF_RANGE;
3531  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3532  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3533  "Vertical scrollbar is visible, should be invisible.\n");
3534  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3535  "reported page/range is %d (%d..%d)\n",
3536  si.nPage, si.nMin, si.nMax);
3537 
3538  /* Again, EM_SCROLL, with SB_LINEUP */
3539  SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3540  memset(&si, 0, sizeof(si));
3541  si.cbSize = sizeof(si);
3542  si.fMask = SIF_PAGE | SIF_RANGE;
3543  GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3544  ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3545  "Vertical scrollbar is invisible, should be visible.\n");
3546  ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3547  "reported page/range is %d (%d..%d)\n",
3548  si.nPage, si.nMin, si.nMax);
3549 
3550  DestroyWindow(hwndRichEdit);
3551 
3552  /* This window proc models what is going on with Corman Lisp 3.0.
3553  At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3554  force the scrollbar into visibility. Recursion should NOT happen
3555  as a result of this action.
3556  */
3558  if (r) {
3559  richeditProc = cls.lpfnWndProc;
3561  cls.lpszClassName = "RicheditStupidOverride";
3562  if(!RegisterClassA(&cls)) assert(0);
3563 
3564  recursionLevel = 0;
3567  hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3569  "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3570 
3571  recursionLevel = 0;
3574  MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3576  "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3577 
3578  /* Unblock window in order to process WM_DESTROY */
3579  recursionLevel = 0;
3582  DestroyWindow(hwndRichEdit);
3583  }
3584 }
3585 
3586 static void test_EM_SETUNDOLIMIT(void)
3587 {
3588  /* cases we test for:
3589  * default behaviour - limiting at 100 undo's
3590  * undo disabled - setting a limit of 0
3591  * undo limited - undo limit set to some to some number, like 2
3592  * bad input - sending a negative number should default to 100 undo's */
3593 
3594  HWND hwndRichEdit = new_richedit(NULL);
3595  CHARRANGE cr;
3596  int i;
3597  int result;
3598 
3599  SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3600  cr.cpMin = 0;
3601  cr.cpMax = -1;
3602  SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3603 
3604  SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3605  /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3606  also, multiple pastes don't combine like WM_CHAR would */
3607 
3608  /* first case - check the default */
3609  SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3610  for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3611  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3612  for (i=0; i<100; i++) /* Undo 100 of them */
3613  SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3614  ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3615  "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3616 
3617  /* second case - cannot undo */
3618  SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3619  SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3620  SendMessageA(hwndRichEdit,
3621  WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3622  ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3623  "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3624 
3625  /* third case - set it to an arbitrary number */
3626  SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3627  SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3628  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3629  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3630  SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3631  /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3632  ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3633  "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3634  SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3635  ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3636  "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3637  SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3638  ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3639  "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3640 
3641  /* fourth case - setting negative numbers should default to 100 undos */
3642  SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3643  result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3644  ok (result == 100,
3645  "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3646 
3647  DestroyWindow(hwndRichEdit);
3648 }
3649 
3650 static void test_ES_PASSWORD(void)
3651 {
3652  /* This isn't hugely testable, so we're just going to run it through its paces */
3653 
3654  HWND hwndRichEdit = new_richedit(NULL);
3655  WCHAR result;
3656 
3657  /* First, check the default of a regular control */
3658  result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3659  ok (result == 0,
3660  "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3661 
3662  /* Now, set it to something normal */
3663  SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3664  result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3665  ok (result == 120,
3666  "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3667 
3668  /* Now, set it to something odd */
3669  SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3670  result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3671  ok (result == 1234,
3672  "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3673  DestroyWindow(hwndRichEdit);
3674 }
3675 
3677 
3679  LPBYTE pbBuff,
3680  LONG cb,
3681  LONG *pcb)
3682 {
3683  char** str = (char**)dwCookie;
3684  *pcb = cb;
3685  if (*pcb > 0) {
3686  memcpy(*str, pbBuff, *pcb);
3687  *str += *pcb;
3688  }
3689  streamout_written = *pcb;
3690  return 0;
3691 }
3692 
3693 static void test_WM_SETTEXT(void)
3694 {
3695  HWND hwndRichEdit = new_richedit(NULL);
3696  const char * TestItem1 = "TestSomeText";
3697  const char * TestItem2 = "TestSomeText\r";
3698  const char * TestItem2_after = "TestSomeText\r\n";
3699  const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3700  const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3701  const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3702  const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3703  const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3704  const char * TestItem5_after = "TestSomeText TestSomeText";
3705  const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3706  const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3707  const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3708  const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3709 
3710  const char rtftextA[] = "{\\rtf sometext}";
3711  const char urtftextA[] = "{\\urtf sometext}";
3712  const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3713  const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3714  const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3715 
3716  char buf[1024] = {0};
3717  WCHAR bufW[1024] = {0};
3718  LRESULT result;
3719 
3720  /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3721  any solitary \r to be converted to \r\n on return. Properly paired
3722  \r\n are not affected. It also shows that the special sequence \r\r\n
3723  gets converted to a single space.
3724  */
3725 
3726 #define TEST_SETTEXT(a, b) \
3727  result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3728  ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3729  result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3730  ok (result == lstrlenA(buf), \
3731  "WM_GETTEXT returned %ld instead of expected %u\n", \
3732  result, lstrlenA(buf)); \
3733  result = strcmp(b, buf); \
3734  ok(result == 0, \
3735  "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3736 
3737  TEST_SETTEXT(TestItem1, TestItem1)
3738  TEST_SETTEXT(TestItem2, TestItem2_after)
3739  TEST_SETTEXT(TestItem3, TestItem3_after)
3740  TEST_SETTEXT(TestItem3_after, TestItem3_after)
3741  TEST_SETTEXT(TestItem4, TestItem4_after)
3742  TEST_SETTEXT(TestItem5, TestItem5_after)
3743  TEST_SETTEXT(TestItem6, TestItem6_after)
3744  TEST_SETTEXT(TestItem7, TestItem7_after)
3745 
3746  /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3747  TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3748  TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3749  TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3750  TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3751  DestroyWindow(hwndRichEdit);
3752 #undef TEST_SETTEXT
3753 
3754 #define TEST_SETTEXTW(a, b) \
3755  result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3756  ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3757  result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3758  ok (result == lstrlenW(bufW), \
3759  "WM_GETTEXT returned %ld instead of expected %u\n", \
3760  result, lstrlenW(bufW)); \
3761  result = lstrcmpW(b, bufW); \
3762  ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3763 
3764  hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3766  0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3767  ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3768  TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3769  TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3770  TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3771  TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3772  DestroyWindow(hwndRichEdit);
3773 #undef TEST_SETTEXTW
3774 
3775  /* Single-line richedit */
3776  hwndRichEdit = new_richedit_with_style(NULL, 0);
3777  result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3778  ok(result == 1, "WM_SETTEXT returned %ld, expected 12\n", result);
3779  result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3780  ok(result == 5, "WM_GETTEXT returned %ld, expected 5\n", result);
3781  ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3782  result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3783  ok(result == 1, "WM_SETTEXT returned %ld, expected 1\n", result);
3784  result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3785  ok(result == 3, "WM_GETTEXT returned %ld, expected 3\n", result);
3786  ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3787  DestroyWindow(hwndRichEdit);
3788 }
3789 
3790 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3791  resent to the callkack. */
3793  LPBYTE pbBuff,
3794  LONG cb,
3795  LONG *pcb)
3796 {
3797  char** str = (char**)dwCookie;
3798  ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3799  *pcb = 0;
3800  if (cb > 0) {
3801  memcpy(*str, pbBuff, cb);
3802  *str += cb;
3803  *pcb = 1;
3804  }
3805  return 0;
3806 }
3807 
3808 static int count_pars(const char *buf)
3809 {
3810  const char *p = buf;
3811  int count = 0;
3812  while ((p = strstr( p, "\\par" )) != NULL)
3813  {
3814  if (!isalpha( p[4] ))
3815  count++;
3816  p++;
3817  }
3818  return count;
3819 }
3820 
3821 static void