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