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 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
34
35 static HMODULE hmoduleRichEdit;
36
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 HWND hwnd;
39 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41 hmoduleRichEdit, NULL);
42 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
43 return hwnd;
44 }
45
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
48 }
49
50 static const char haystack[] = "WINEWine wineWine wine WineWine";
51 /* ^0 ^10 ^20 ^30 */
52
53 struct find_s {
54 int start;
55 int end;
56 const char *needle;
57 int flags;
58 int expected_loc;
59 int _todo_wine;
60 };
61
62
63 struct find_s find_tests[] = {
64 /* Find in empty text */
65 {0, -1, "foo", FR_DOWN, -1, 0},
66 {0, -1, "foo", 0, -1, 0},
67 {0, -1, "", FR_DOWN, -1, 0},
68 {20, 5, "foo", FR_DOWN, -1, 0},
69 {5, 20, "foo", FR_DOWN, -1, 0}
70 };
71
72 struct find_s find_tests2[] = {
73 /* No-result find */
74 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
75 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
76
77 /* Subsequent finds */
78 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
79 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
80 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
81 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
82
83 /* Find backwards */
84 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
85 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
86 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
87
88 /* Case-insensitive */
89 {1, 31, "wInE", FR_DOWN, 4, 0},
90 {1, 31, "Wine", FR_DOWN, 4, 0},
91
92 /* High-to-low ranges */
93 {20, 5, "Wine", FR_DOWN, -1, 0},
94 {2, 1, "Wine", FR_DOWN, -1, 0},
95 {30, 29, "Wine", FR_DOWN, -1, 0},
96 {20, 5, "Wine", 0, 13, 0},
97
98 /* Find nothing */
99 {5, 10, "", FR_DOWN, -1, 0},
100 {10, 5, "", FR_DOWN, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {10, 5, "", 0, -1, 0},
103
104 /* Whole-word search */
105 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
106 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
107 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
108 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
109 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
110 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
111 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
112
113 /* Bad ranges */
114 {5, 200, "XXX", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-20, 20, "Wine", FR_DOWN, -1, 0},
117 {-15, -20, "Wine", FR_DOWN, -1, 0},
118 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
119
120 /* Check the case noted in bug 4479 where matches at end aren't recognized */
121 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
122 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
124 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
126
127 /* The backwards case of bug 4479; bounds look right
128 * Fails because backward find is wrong */
129 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
130 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
131
132 {0, -1, "wineWine wine", 0, -1, 0},
133 };
134
135 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
136 int findloc;
137 FINDTEXT ft;
138 memset(&ft, 0, sizeof(ft));
139 ft.chrg.cpMin = f->start;
140 ft.chrg.cpMax = f->end;
141 ft.lpstrText = f->needle;
142 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
143 ok(findloc == f->expected_loc,
144 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
145 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
146 }
147
148 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
149 int id) {
150 int findloc;
151 FINDTEXTEX ft;
152 int expected_end_loc;
153
154 memset(&ft, 0, sizeof(ft));
155 ft.chrg.cpMin = f->start;
156 ft.chrg.cpMax = f->end;
157 ft.lpstrText = f->needle;
158 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
159 ok(findloc == f->expected_loc,
160 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
161 name, id, f->needle, f->start, f->end, f->flags, findloc);
162 ok(ft.chrgText.cpMin == f->expected_loc,
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
165 expected_end_loc = ((f->expected_loc == -1) ? -1
166 : f->expected_loc + strlen(f->needle));
167 ok(ft.chrgText.cpMax == expected_end_loc,
168 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
169 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
170 }
171
172 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
173 int num_tests)
174 {
175 int i;
176
177 for (i = 0; i < num_tests; i++) {
178 if (find[i]._todo_wine) {
179 todo_wine {
180 check_EM_FINDTEXT(hwnd, name, &find[i], i);
181 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
182 }
183 } else {
184 check_EM_FINDTEXT(hwnd, name, &find[i], i);
185 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
186 }
187 }
188 }
189
190 static void test_EM_FINDTEXT(void)
191 {
192 HWND hwndRichEdit = new_richedit(NULL);
193 CHARFORMAT2 cf2;
194
195 /* Empty rich edit control */
196 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
197 sizeof(find_tests)/sizeof(struct find_s));
198
199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
200
201 /* Haystack text */
202 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
203 sizeof(find_tests2)/sizeof(struct find_s));
204
205 /* Setting a format on an arbitrary range should have no effect in search
206 results. This tests correct offset reporting across runs. */
207 cf2.cbSize = sizeof(CHARFORMAT2);
208 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
209 (LPARAM) &cf2);
210 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
211 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
212 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
213 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
214
215 /* Haystack text, again */
216 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
217 sizeof(find_tests2)/sizeof(struct find_s));
218
219 /* Yet another range */
220 cf2.dwMask = CFM_BOLD | cf2.dwMask;
221 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
222 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
223 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
224
225 /* Haystack text, again */
226 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
227 sizeof(find_tests2)/sizeof(struct find_s));
228
229 DestroyWindow(hwndRichEdit);
230 }
231
232 static const struct getline_s {
233 int line;
234 size_t buffer_len;
235 const char *text;
236 } gl[] = {
237 {0, 10, "foo bar\r"},
238 {1, 10, "\r"},
239 {2, 10, "bar\r"},
240 {3, 10, "\r"},
241
242 /* Buffer smaller than line length */
243 {0, 2, "foo bar\r"},
244 {0, 1, "foo bar\r"},
245 {0, 0, "foo bar\r"}
246 };
247
248 static void test_EM_GETLINE(void)
249 {
250 int i;
251 HWND hwndRichEdit = new_richedit(NULL);
252 static const int nBuf = 1024;
253 char dest[1024], origdest[1024];
254 const char text[] = "foo bar\n"
255 "\n"
256 "bar\n";
257
258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
259
260 memset(origdest, 0xBB, nBuf);
261 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
262 {
263 int nCopied;
264 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
265 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
266 memset(dest, 0xBB, nBuf);
267 *(WORD *) dest = gl[i].buffer_len;
268
269 /* EM_GETLINE appends a "\r\0" to the end of the line
270 * nCopied counts up to and including the '\r' */
271 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
272 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
273 expected_nCopied);
274 /* two special cases since a parameter is passed via dest */
275 if (gl[i].buffer_len == 0)
276 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
277 "buffer_len=0\n");
278 else if (gl[i].buffer_len == 1)
279 ok(dest[0] == gl[i].text[0] && !dest[1] &&
280 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
281 else
282 {
283 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
284 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
285 ok(!strncmp(dest + expected_bytes_written, origdest
286 + expected_bytes_written, nBuf - expected_bytes_written),
287 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
288 }
289 }
290
291 DestroyWindow(hwndRichEdit);
292 }
293
294 static void test_EM_LINELENGTH(void)
295 {
296 HWND hwndRichEdit = new_richedit(NULL);
297 const char * text =
298 "richedit1\r"
299 "richedit1\n"
300 "richedit1\r\n"
301 "richedit1";
302 int offset_test[10][2] = {
303 {0, 9},
304 {5, 9},
305 {10, 9},
306 {15, 9},
307 {20, 9},
308 {25, 9},
309 {30, 9},
310 {35, 9},
311 {40, 0},
312 {45, 0},
313 };
314 int i;
315 LRESULT result;
316
317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
318
319 for (i = 0; i < 10; i++) {
320 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
321 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
322 offset_test[i][0], result, offset_test[i][1]);
323 }
324
325 DestroyWindow(hwndRichEdit);
326 }
327
328 static int get_scroll_pos_y(HWND hwnd)
329 {
330 POINT p = {-1, -1};
331 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
332 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
333 return p.y;
334 }
335
336 static void move_cursor(HWND hwnd, long charindex)
337 {
338 CHARRANGE cr;
339 cr.cpMax = charindex;
340 cr.cpMin = charindex;
341 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
342 }
343
344 static void line_scroll(HWND hwnd, int amount)
345 {
346 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
347 }
348
349 static void test_EM_SCROLLCARET(void)
350 {
351 int prevY, curY;
352 HWND hwndRichEdit = new_richedit(NULL);
353 const char text[] = "aa\n"
354 "this is a long line of text that should be longer than the "
355 "control's width\n"
356 "cc\n"
357 "dd\n"
358 "ee\n"
359 "ff\n"
360 "gg\n"
361 "hh\n";
362
363 /* Can't verify this */
364 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
365
366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
367
368 /* Caret above visible window */
369 line_scroll(hwndRichEdit, 3);
370 prevY = get_scroll_pos_y(hwndRichEdit);
371 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
372 curY = get_scroll_pos_y(hwndRichEdit);
373 ok(prevY != curY, "%d == %d\n", prevY, curY);
374
375 /* Caret below visible window */
376 move_cursor(hwndRichEdit, sizeof(text) - 1);
377 line_scroll(hwndRichEdit, -3);
378 prevY = get_scroll_pos_y(hwndRichEdit);
379 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
380 curY = get_scroll_pos_y(hwndRichEdit);
381 ok(prevY != curY, "%d == %d\n", prevY, curY);
382
383 /* Caret in visible window */
384 move_cursor(hwndRichEdit, sizeof(text) - 2);
385 prevY = get_scroll_pos_y(hwndRichEdit);
386 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
387 curY = get_scroll_pos_y(hwndRichEdit);
388 ok(prevY == curY, "%d != %d\n", prevY, curY);
389
390 /* Caret still in visible window */
391 line_scroll(hwndRichEdit, -1);
392 prevY = get_scroll_pos_y(hwndRichEdit);
393 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
394 curY = get_scroll_pos_y(hwndRichEdit);
395 ok(prevY == curY, "%d != %d\n", prevY, curY);
396
397 DestroyWindow(hwndRichEdit);
398 }
399
400 static void test_EM_POSFROMCHAR(void)
401 {
402 HWND hwndRichEdit = new_richedit(NULL);
403 int i;
404 LRESULT result;
405 unsigned int height = 0;
406 int xpos = 0;
407 static const char text[] = "aa\n"
408 "this is a long line of text that should be longer than the "
409 "control's width\n"
410 "cc\n"
411 "dd\n"
412 "ee\n"
413 "ff\n"
414 "gg\n"
415 "hh\n";
416
417 /* Fill the control to lines to ensure that most of them are offscreen */
418 for (i = 0; i < 50; i++)
419 {
420 /* Do not modify the string; it is exactly 16 characters long. */
421 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
422 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
423 }
424
425 /*
426 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
427 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
428 Richedit 3.0 accepts either of the above API conventions.
429 */
430
431 /* Testing Richedit 2.0 API format */
432
433 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
434 Since all lines are identical and drawn with the same font,
435 they should have the same height... right?
436 */
437 for (i = 0; i < 50; i++)
438 {
439 /* All the lines are 16 characters long */
440 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
441 if (i == 0)
442 {
443 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
444 todo_wine {
445 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
446 }
447 xpos = LOWORD(result);
448 }
449 else if (i == 1)
450 {
451 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
452 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
453 height = HIWORD(result);
454 }
455 else
456 {
457 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
458 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
459 }
460 }
461
462 /* Testing position at end of text */
463 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
464 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
465 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
466
467 /* Testing position way past end of text */
468 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
469 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
470 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
471
472 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
473 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
474 for (i = 0; i < 50; i++)
475 {
476 /* All the lines are 16 characters long */
477 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
478 ok((signed short)(HIWORD(result)) == (i - 1) * height,
479 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
480 (signed short)(HIWORD(result)), (i - 1) * height);
481 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
482 }
483
484 /* Testing position at end of text */
485 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
486 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
487 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
488
489 /* Testing position way past end of text */
490 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
491 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
492 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
493
494 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
496 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
497
498 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
499 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
500 todo_wine {
501 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
502 }
503 xpos = LOWORD(result);
504
505 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
506 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
507 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
508 todo_wine {
509 /* Fails on builtin because horizontal scrollbar is not being shown */
510 ok((signed short)(LOWORD(result)) < xpos,
511 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
512 (signed short)(LOWORD(result)), xpos);
513 }
514 DestroyWindow(hwndRichEdit);
515 }
516
517 static void test_EM_SETCHARFORMAT(void)
518 {
519 HWND hwndRichEdit = new_richedit(NULL);
520 CHARFORMAT2 cf2;
521 int rc = 0;
522 int tested_effects[] = {
523 CFE_BOLD,
524 CFE_ITALIC,
525 CFE_UNDERLINE,
526 CFE_STRIKEOUT,
527 CFE_PROTECTED,
528 CFE_LINK,
529 CFE_SUBSCRIPT,
530 CFE_SUPERSCRIPT,
531 0
532 };
533 int i;
534 CHARRANGE cr;
535
536 /* Invalid flags, CHARFORMAT2 structure blanked out */
537 memset(&cf2, 0, sizeof(cf2));
538 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
539 (LPARAM) &cf2);
540 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
541
542 /* A valid flag, CHARFORMAT2 structure blanked out */
543 memset(&cf2, 0, sizeof(cf2));
544 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
545 (LPARAM) &cf2);
546 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
547
548 /* A valid flag, CHARFORMAT2 structure blanked out */
549 memset(&cf2, 0, sizeof(cf2));
550 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
551 (LPARAM) &cf2);
552 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
553
554 /* A valid flag, CHARFORMAT2 structure blanked out */
555 memset(&cf2, 0, sizeof(cf2));
556 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
557 (LPARAM) &cf2);
558 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
559
560 /* A valid flag, CHARFORMAT2 structure blanked out */
561 memset(&cf2, 0, sizeof(cf2));
562 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
563 (LPARAM) &cf2);
564 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
565
566 /* Invalid flags, CHARFORMAT2 structure minimally filled */
567 memset(&cf2, 0, sizeof(cf2));
568 cf2.cbSize = sizeof(CHARFORMAT2);
569 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
570 (LPARAM) &cf2);
571 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
572 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
573 ok(rc == FALSE, "Should not be able to undo here.\n");
574 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
575
576 /* A valid flag, CHARFORMAT2 structure minimally filled */
577 memset(&cf2, 0, sizeof(cf2));
578 cf2.cbSize = sizeof(CHARFORMAT2);
579 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
580 (LPARAM) &cf2);
581 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
582 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
583 todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
584 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
585
586 /* A valid flag, CHARFORMAT2 structure minimally filled */
587 memset(&cf2, 0, sizeof(cf2));
588 cf2.cbSize = sizeof(CHARFORMAT2);
589 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
590 (LPARAM) &cf2);
591 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
592 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
593 ok(rc == FALSE, "Should not be able to undo here.\n");
594 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
595
596 /* A valid flag, CHARFORMAT2 structure minimally filled */
597 memset(&cf2, 0, sizeof(cf2));
598 cf2.cbSize = sizeof(CHARFORMAT2);
599 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600 (LPARAM) &cf2);
601 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
602 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
603 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
604 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
605
606 /* A valid flag, CHARFORMAT2 structure minimally filled */
607 memset(&cf2, 0, sizeof(cf2));
608 cf2.cbSize = sizeof(CHARFORMAT2);
609 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
610 (LPARAM) &cf2);
611 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
612 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
613 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
614 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
615
616 cf2.cbSize = sizeof(CHARFORMAT2);
617 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
618 (LPARAM) &cf2);
619
620 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
621 cf2.cbSize = sizeof(CHARFORMAT2);
622 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623 (LPARAM) &cf2);
624 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
625 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
626
627 /* wParam==0 is default char format, does not set modify */
628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
629 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
630 ok(rc == 0, "Text marked as modified, expected not modified!\n");
631 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
632 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
633 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
634 ok(rc == 0, "Text marked as modified, expected not modified!\n");
635
636 /* wParam==SCF_SELECTION sets modify if nonempty selection */
637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
638 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
639 ok(rc == 0, "Text marked as modified, expected not modified!\n");
640 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
641 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
642 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
643 ok(rc == 0, "Text marked as modified, expected not modified!\n");
644
645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
646 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
647 ok(rc == 0, "Text marked as modified, expected not modified!\n");
648 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
649 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
650 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
651 ok(rc == 0, "Text marked as modified, expected not modified!\n");
652 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
653 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
654 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
656 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
657
658 /* wParam==SCF_ALL sets modify regardless of whether text is present */
659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
660 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
661 ok(rc == 0, "Text marked as modified, expected not modified!\n");
662 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
663 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
664 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
665 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
666
667 DestroyWindow(hwndRichEdit);
668
669 /* EM_GETCHARFORMAT tests */
670 for (i = 0; tested_effects[i]; i++)
671 {
672 hwndRichEdit = new_richedit(NULL);
673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
674
675 /* Need to set a TrueType font to get consistent CFM_BOLD results */
676 memset(&cf2, 0, sizeof(CHARFORMAT2));
677 cf2.cbSize = sizeof(CHARFORMAT2);
678 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
679 cf2.dwEffects = 0;
680 strcpy(cf2.szFaceName, "Courier New");
681 cf2.wWeight = FW_DONTCARE;
682 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
683
684 memset(&cf2, 0, sizeof(CHARFORMAT2));
685 cf2.cbSize = sizeof(CHARFORMAT2);
686 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
687 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
688 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
689 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
690 ||
691 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
692 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
693 ok((cf2.dwEffects & tested_effects[i]) == 0,
694 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
695
696 memset(&cf2, 0, sizeof(CHARFORMAT2));
697 cf2.cbSize = sizeof(CHARFORMAT2);
698 cf2.dwMask = tested_effects[i];
699 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
700 cf2.dwMask = CFM_SUPERSCRIPT;
701 cf2.dwEffects = tested_effects[i];
702 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
703 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
704
705 memset(&cf2, 0, sizeof(CHARFORMAT2));
706 cf2.cbSize = sizeof(CHARFORMAT2);
707 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
708 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
709 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
710 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
711 ||
712 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
713 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
714 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
715 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
716
717 memset(&cf2, 0, sizeof(CHARFORMAT2));
718 cf2.cbSize = sizeof(CHARFORMAT2);
719 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
720 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
721 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
722 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
723 ||
724 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
725 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
726 ok((cf2.dwEffects & tested_effects[i]) == 0,
727 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
728
729 memset(&cf2, 0, sizeof(CHARFORMAT2));
730 cf2.cbSize = sizeof(CHARFORMAT2);
731 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
732 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
733 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
734 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
735 ||
736 (cf2.dwMask & tested_effects[i]) == 0),
737 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
738
739 DestroyWindow(hwndRichEdit);
740 }
741
742 for (i = 0; tested_effects[i]; i++)
743 {
744 hwndRichEdit = new_richedit(NULL);
745 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
746
747 /* Need to set a TrueType font to get consistent CFM_BOLD results */
748 memset(&cf2, 0, sizeof(CHARFORMAT2));
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
751 cf2.dwEffects = 0;
752 strcpy(cf2.szFaceName, "Courier New");
753 cf2.wWeight = FW_DONTCARE;
754 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
755
756 memset(&cf2, 0, sizeof(CHARFORMAT2));
757 cf2.cbSize = sizeof(CHARFORMAT2);
758 cf2.dwMask = tested_effects[i];
759 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
760 cf2.dwMask = CFM_SUPERSCRIPT;
761 cf2.dwEffects = tested_effects[i];
762 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
764
765 memset(&cf2, 0, sizeof(CHARFORMAT2));
766 cf2.cbSize = sizeof(CHARFORMAT2);
767 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
768 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
769 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
770 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
771 ||
772 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
773 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
774 ok((cf2.dwEffects & tested_effects[i]) == 0,
775 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
776
777 memset(&cf2, 0, sizeof(CHARFORMAT2));
778 cf2.cbSize = sizeof(CHARFORMAT2);
779 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
780 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
781 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
782 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
783 ||
784 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
785 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
786 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
787 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
788
789 memset(&cf2, 0, sizeof(CHARFORMAT2));
790 cf2.cbSize = sizeof(CHARFORMAT2);
791 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
792 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
793 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
794 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
795 ||
796 (cf2.dwMask & tested_effects[i]) == 0),
797 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
798 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
799 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
800
801 DestroyWindow(hwndRichEdit);
802 }
803
804 /* Effects applied on an empty selection should take effect when selection is
805 replaced with text */
806 hwndRichEdit = new_richedit(NULL);
807 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
808 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
809
810 memset(&cf2, 0, sizeof(CHARFORMAT2));
811 cf2.cbSize = sizeof(CHARFORMAT2);
812 cf2.dwMask = CFM_BOLD;
813 cf2.dwEffects = CFE_BOLD;
814 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
815
816 /* Selection is now nonempty */
817 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
818
819 memset(&cf2, 0, sizeof(CHARFORMAT2));
820 cf2.cbSize = sizeof(CHARFORMAT2);
821 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
822 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
823
824 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
826 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
828
829
830 /* Set two effects on an empty selection */
831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
832 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
833
834 memset(&cf2, 0, sizeof(CHARFORMAT2));
835 cf2.cbSize = sizeof(CHARFORMAT2);
836 cf2.dwMask = CFM_BOLD;
837 cf2.dwEffects = CFE_BOLD;
838 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
839 cf2.dwMask = CFM_ITALIC;
840 cf2.dwEffects = CFE_ITALIC;
841 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842
843 /* Selection is now nonempty */
844 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
845
846 memset(&cf2, 0, sizeof(CHARFORMAT2));
847 cf2.cbSize = sizeof(CHARFORMAT2);
848 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
849 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
850
851 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
852 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
853 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
854 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
855
856 /* Setting the (empty) selection to exactly the same place as before should
857 NOT clear the insertion style! */
858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
859 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
860
861 memset(&cf2, 0, sizeof(CHARFORMAT2));
862 cf2.cbSize = sizeof(CHARFORMAT2);
863 cf2.dwMask = CFM_BOLD;
864 cf2.dwEffects = CFE_BOLD;
865 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866
867 /* Empty selection in same place, insert style should NOT be forgotten here. */
868 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
869
870 /* Selection is now nonempty */
871 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
872
873 memset(&cf2, 0, sizeof(CHARFORMAT2));
874 cf2.cbSize = sizeof(CHARFORMAT2);
875 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
876 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
877
878 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
879 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
880 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
881 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
882
883 /* Ditto with EM_EXSETSEL */
884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
885 cr.cpMin = 2; cr.cpMax = 2;
886 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
887
888 memset(&cf2, 0, sizeof(CHARFORMAT2));
889 cf2.cbSize = sizeof(CHARFORMAT2);
890 cf2.dwMask = CFM_BOLD;
891 cf2.dwEffects = CFE_BOLD;
892 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
893
894 /* Empty selection in same place, insert style should NOT be forgotten here. */
895 cr.cpMin = 2; cr.cpMax = 2;
896 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
897
898 /* Selection is now nonempty */
899 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
900
901 memset(&cf2, 0, sizeof(CHARFORMAT2));
902 cf2.cbSize = sizeof(CHARFORMAT2);
903 cr.cpMin = 2; cr.cpMax = 6;
904 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
905 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
906
907 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
908 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
909 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
910 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
911
912 DestroyWindow(hwndRichEdit);
913 }
914
915 static void test_EM_SETTEXTMODE(void)
916 {
917 HWND hwndRichEdit = new_richedit(NULL);
918 CHARFORMAT2 cf2, cf2test;
919 CHARRANGE cr;
920 int rc = 0;
921
922 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
923 /*Insert text into the control*/
924
925 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
926
927 /*Attempt to change the control to plain text mode*/
928 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
929 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
930
931 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
932 If rich text is pasted, it should have the same formatting as the rest
933 of the text in the control*/
934
935 /*Italicize the text
936 *NOTE: If the default text was already italicized, the test will simply
937 reverse; in other words, it will copy a regular "wine" into a plain
938 text window that uses an italicized format*/
939 cf2.cbSize = sizeof(CHARFORMAT2);
940 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
941 (LPARAM) &cf2);
942
943 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
944 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
945
946 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
947 ok(rc == 0, "Text marked as modified, expected not modified!\n");
948
949 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
950 however, SCF_ALL has been implemented*/
951 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
952 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
953
954 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
955 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
956
957 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
958
959 /*Select the string "wine"*/
960 cr.cpMin = 0;
961 cr.cpMax = 4;
962 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
963
964 /*Copy the italicized "wine" to the clipboard*/
965 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
966
967 /*Reset the formatting to default*/
968 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
969 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
970 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
971
972 /*Clear the text in the control*/
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
974
975 /*Switch to Plain Text Mode*/
976 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
977 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
978
979 /*Input "wine" again in normal format*/
980 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
981
982 /*Paste the italicized "wine" into the control*/
983 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
984
985 /*Select a character from the first "wine" string*/
986 cr.cpMin = 2;
987 cr.cpMax = 3;
988 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
989
990 /*Retrieve its formatting*/
991 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
992 (LPARAM) &cf2);
993
994 /*Select a character from the second "wine" string*/
995 cr.cpMin = 5;
996 cr.cpMax = 6;
997 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
998
999 /*Retrieve its formatting*/
1000 cf2test.cbSize = sizeof(CHARFORMAT2);
1001 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1002 (LPARAM) &cf2test);
1003
1004 /*Compare the two formattings*/
1005 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1006 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1007 cf2.dwEffects, cf2test.dwEffects);
1008 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1009 printing "wine" in the current format(normal)
1010 pasting "wine" from the clipboard(italicized)
1011 comparing the two formats(should differ)*/
1012
1013 /*Attempt to switch with text in control*/
1014 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1015 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1016
1017 /*Clear control*/
1018 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1019
1020 /*Switch into Rich Text mode*/
1021 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1022 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1023
1024 /*Print "wine" in normal formatting into the control*/
1025 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1026
1027 /*Paste italicized "wine" into the control*/
1028 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1029
1030 /*Select text from the first "wine" string*/
1031 cr.cpMin = 1;
1032 cr.cpMax = 3;
1033 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1034
1035 /*Retrieve its formatting*/
1036 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1037 (LPARAM) &cf2);
1038
1039 /*Select text from the second "wine" string*/
1040 cr.cpMin = 6;
1041 cr.cpMax = 7;
1042 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1043
1044 /*Retrieve its formatting*/
1045 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1046 (LPARAM) &cf2test);
1047
1048 /*Test that the two formattings are not the same*/
1049 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1050 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1051 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1052
1053 DestroyWindow(hwndRichEdit);
1054 }
1055
1056 static void test_TM_PLAINTEXT(void)
1057 {
1058 /*Tests plain text properties*/
1059
1060 HWND hwndRichEdit = new_richedit(NULL);
1061 CHARFORMAT2 cf2, cf2test;
1062 CHARRANGE cr;
1063 int rc = 0;
1064
1065 /*Switch to plain text mode*/
1066
1067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1068 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1069
1070 /*Fill control with text*/
1071
1072 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1073
1074 /*Select some text and bold it*/
1075
1076 cr.cpMin = 10;
1077 cr.cpMax = 20;
1078 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1079 cf2.cbSize = sizeof(CHARFORMAT2);
1080 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1081 (LPARAM) &cf2);
1082
1083 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1084 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1085
1086 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1087 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1088
1089 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1090 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1091
1092 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1093 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1094
1095 /*Get the formatting of those characters*/
1096
1097 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1098
1099 /*Get the formatting of some other characters*/
1100 cf2test.cbSize = sizeof(CHARFORMAT2);
1101 cr.cpMin = 21;
1102 cr.cpMax = 30;
1103 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1104 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1105
1106 /*Test that they are the same as plain text allows only one formatting*/
1107
1108 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1109 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1110 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1111
1112 /*Fill the control with a "wine" string, which when inserted will be bold*/
1113
1114 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1115
1116 /*Copy the bolded "wine" string*/
1117
1118 cr.cpMin = 0;
1119 cr.cpMax = 4;
1120 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1122
1123 /*Swap back to rich text*/
1124
1125 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1126 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1127
1128 /*Set the default formatting to bold italics*/
1129
1130 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1131 cf2.dwMask |= CFM_ITALIC;
1132 cf2.dwEffects ^= CFE_ITALIC;
1133 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1134 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1135
1136 /*Set the text in the control to "wine", which will be bold and italicized*/
1137
1138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1139
1140 /*Paste the plain text "wine" string, which should take the insert
1141 formatting, which at the moment is bold italics*/
1142
1143 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1144
1145 /*Select the first "wine" string and retrieve its formatting*/
1146
1147 cr.cpMin = 1;
1148 cr.cpMax = 3;
1149 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1150 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1151
1152 /*Select the second "wine" string and retrieve its formatting*/
1153
1154 cr.cpMin = 5;
1155 cr.cpMax = 7;
1156 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1157 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1158
1159 /*Compare the two formattings. They should be the same.*/
1160
1161 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1162 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1163 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1164 DestroyWindow(hwndRichEdit);
1165 }
1166
1167 static void test_WM_GETTEXT(void)
1168 {
1169 HWND hwndRichEdit = new_richedit(NULL);
1170 static const char text[] = "Hello. My name is RichEdit!";
1171 static const char text2[] = "Hello. My name is RichEdit!\r";
1172 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1173 char buffer[1024] = {0};
1174 int result;
1175
1176 /* Baseline test with normal-sized buffer */
1177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1178 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1179 ok(result == lstrlen(buffer),
1180 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1181 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1182 result = strcmp(buffer,text);
1183 ok(result == 0,
1184 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1185
1186 /* Test for returned value of WM_GETTEXTLENGTH */
1187 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1188 ok(result == lstrlen(text),
1189 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1190 result, lstrlen(text));
1191
1192 /* Test for behavior in overflow case */
1193 memset(buffer, 0, 1024);
1194 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1195 ok(result == 0 ||
1196 result == lstrlenA(text) - 1, /* XP, win2k3 */
1197 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1198 result = strcmp(buffer,text);
1199 if (result)
1200 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1201 ok(result == 0,
1202 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1203
1204 /* Baseline test with normal-sized buffer and carriage return */
1205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1206 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1207 ok(result == lstrlen(buffer),
1208 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1209 result = strcmp(buffer,text2_after);
1210 ok(result == 0,
1211 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1212
1213 /* Test for returned value of WM_GETTEXTLENGTH */
1214 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1215 ok(result == lstrlen(text2_after),
1216 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1217 result, lstrlen(text2_after));
1218
1219 /* Test for behavior of CRLF conversion in case of overflow */
1220 memset(buffer, 0, 1024);
1221 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1222 ok(result == 0 ||
1223 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1224 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1225 result = strcmp(buffer,text2);
1226 if (result)
1227 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1228 ok(result == 0,
1229 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1230
1231 DestroyWindow(hwndRichEdit);
1232 }
1233
1234 static void test_EM_GETTEXTRANGE(void)
1235 {
1236 HWND hwndRichEdit = new_richedit(NULL);
1237 const char * text1 = "foo bar\r\nfoo bar";
1238 const char * text2 = "foo bar\rfoo bar";
1239 const char * expect = "bar\rfoo";
1240 char buffer[1024] = {0};
1241 LRESULT result;
1242 TEXTRANGEA textRange;
1243
1244 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1245
1246 textRange.lpstrText = buffer;
1247 textRange.chrg.cpMin = 4;
1248 textRange.chrg.cpMax = 11;
1249 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1250 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1251 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1252
1253 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1254
1255 textRange.lpstrText = buffer;
1256 textRange.chrg.cpMin = 4;
1257 textRange.chrg.cpMax = 11;
1258 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1259 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1260 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1261
1262 DestroyWindow(hwndRichEdit);
1263 }
1264
1265 static void test_EM_GETSELTEXT(void)
1266 {
1267 HWND hwndRichEdit = new_richedit(NULL);
1268 const char * text1 = "foo bar\r\nfoo bar";
1269 const char * text2 = "foo bar\rfoo bar";
1270 const char * expect = "bar\rfoo";
1271 char buffer[1024] = {0};
1272 LRESULT result;
1273
1274 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1275
1276 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1277 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1278 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1279 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1280
1281 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1282
1283 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1284 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1285 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1286 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1287
1288 DestroyWindow(hwndRichEdit);
1289 }
1290
1291 /* FIXME: need to test unimplemented options and robustly test wparam */
1292 static void test_EM_SETOPTIONS(void)
1293 {
1294 HWND hwndRichEdit = new_richedit(NULL);
1295 static const char text[] = "Hello. My name is RichEdit!";
1296 char buffer[1024] = {0};
1297
1298 /* NEGATIVE TESTING - NO OPTIONS SET */
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1300 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1301
1302 /* testing no readonly by sending 'a' to the control*/
1303 SetFocus(hwndRichEdit);
1304 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1305 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1306 ok(buffer[0]=='a',
1307 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1309
1310 /* READONLY - sending 'a' to the control */
1311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1312 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1313 SetFocus(hwndRichEdit);
1314 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1315 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1316 ok(buffer[0]==text[0],
1317 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1318
1319 DestroyWindow(hwndRichEdit);
1320 }
1321
1322 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1323 {
1324 CHARFORMAT2W text_format;
1325 text_format.cbSize = sizeof(text_format);
1326 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1327 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1328 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1329 }
1330
1331 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1332 {
1333 int link_present = 0;
1334
1335 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1336 if (is_url)
1337 { /* control text is url; should get CFE_LINK */
1338 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1339 }
1340 else
1341 {
1342 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1343 }
1344 }
1345
1346 static HWND new_static_wnd(HWND parent) {
1347 return new_window("Static", 0, parent);
1348 }
1349
1350 static void test_EM_AUTOURLDETECT(void)
1351 {
1352 struct urls_s {
1353 const char *text;
1354 int is_url;
1355 } urls[12] = {
1356 {"winehq.org", 0},
1357 {"http://www.winehq.org", 1},
1358 {"http//winehq.org", 0},
1359 {"ww.winehq.org", 0},
1360 {"www.winehq.org", 1},
1361 {"ftp://192.168.1.1", 1},
1362 {"ftp//192.168.1.1", 0},
1363 {"mailto:your@email.com", 1},
1364 {"prospero:prosperoserver", 1},
1365 {"telnet:test", 1},
1366 {"news:newserver", 1},
1367 {"wais:waisserver", 1}
1368 };
1369
1370 int i, j;
1371 int urlRet=-1;
1372 HWND hwndRichEdit, parent;
1373
1374 /* All of the following should cause the URL to be detected */
1375 const char * templates_delim[] = {
1376 "This is some text with X on it",
1377 "This is some text with (X) on it",
1378 "This is some text with X\r on it",
1379 "This is some text with ---X--- on it",
1380 "This is some text with \"X\" on it",
1381 "This is some text with 'X' on it",
1382 "This is some text with 'X' on it",
1383 "This is some text with :X: on it",
1384
1385 "This text ends with X",
1386
1387 "This is some text with X) on it",
1388 "This is some text with X--- on it",
1389 "This is some text with X\" on it",
1390 "This is some text with X' on it",
1391 "This is some text with X: on it",
1392
1393 "This is some text with (X on it",
1394 "This is some text with \rX on it",
1395 "This is some text with ---X on it",
1396 "This is some text with \"X on it",
1397 "This is some text with 'X on it",
1398 "This is some text with :X on it",
1399 };
1400 /* None of these should cause the URL to be detected */
1401 const char * templates_non_delim[] = {
1402 "This is some text with |X| on it",
1403 "This is some text with *X* on it",
1404 "This is some text with /X/ on it",
1405 "This is some text with +X+ on it",
1406 "This is some text with %X% on it",
1407 "This is some text with #X# on it",
1408 "This is some text with @X@ on it",
1409 "This is some text with \\X\\ on it",
1410 "This is some text with |X on it",
1411 "This is some text with *X on it",
1412 "This is some text with /X on it",
1413 "This is some text with +X on it",
1414 "This is some text with %X on it",
1415 "This is some text with #X on it",
1416 "This is some text with @X on it",
1417 "This is some text with \\X on it",
1418 };
1419 /* All of these cause the URL detection to be extended by one more byte,
1420 thus demonstrating that the tested character is considered as part
1421 of the URL. */
1422 const char * templates_xten_delim[] = {
1423 "This is some text with X| on it",
1424 "This is some text with X* on it",
1425 "This is some text with X/ on it",
1426 "This is some text with X+ on it",
1427 "This is some text with X% on it",
1428 "This is some text with X# on it",
1429 "This is some text with X@ on it",
1430 "This is some text with X\\ on it",
1431 };
1432 char buffer[1024];
1433 MSG msg;
1434
1435 parent = new_static_wnd(NULL);
1436 hwndRichEdit = new_richedit(parent);
1437 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1438 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1439 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1440 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1441 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1442 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1443 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1444 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1445 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1446 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1447 /* for each url, check the text to see if CFE_LINK effect is present */
1448 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1449
1450 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1451 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1452 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1453
1454 /* Link detection should happen immediately upon WM_SETTEXT */
1455 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1457 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1458 }
1459 DestroyWindow(hwndRichEdit);
1460
1461 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1462 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1463 hwndRichEdit = new_richedit(parent);
1464
1465 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1466 char * at_pos;
1467 int at_offset;
1468 int end_offset;
1469
1470 at_pos = strchr(templates_delim[j], 'X');
1471 at_offset = at_pos - templates_delim[j];
1472 strncpy(buffer, templates_delim[j], at_offset);
1473 buffer[at_offset] = '\0';
1474 strcat(buffer, urls[i].text);
1475 strcat(buffer, templates_delim[j] + at_offset + 1);
1476 end_offset = at_offset + strlen(urls[i].text);
1477
1478 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1479 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1480
1481 /* This assumes no templates start with the URL itself, and that they
1482 have at least two characters before the URL text */
1483 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1484 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1485 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1486 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1487 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1488 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1489
1490 if (urls[i].is_url)
1491 {
1492 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1493 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1494 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1495 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1496 }
1497 else
1498 {
1499 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1500 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1501 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1502 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1503 }
1504 if (buffer[end_offset] != '\0')
1505 {
1506 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1507 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1508 if (buffer[end_offset +1] != '\0')
1509 {
1510 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1511 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1512 }
1513 }
1514 }
1515
1516 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1517 char * at_pos;
1518 int at_offset;
1519 int end_offset;
1520
1521 at_pos = strchr(templates_non_delim[j], 'X');
1522 at_offset = at_pos - templates_non_delim[j];
1523 strncpy(buffer, templates_non_delim[j], at_offset);
1524 buffer[at_offset] = '\0';
1525 strcat(buffer, urls[i].text);
1526 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1527 end_offset = at_offset + strlen(urls[i].text);
1528
1529 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1531
1532 /* This assumes no templates start with the URL itself, and that they
1533 have at least two characters before the URL text */
1534 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1535 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1536 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1537 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1538 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1539 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1540
1541 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1542 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1543 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1544 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1545 if (buffer[end_offset] != '\0')
1546 {
1547 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1548 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1549 if (buffer[end_offset +1] != '\0')
1550 {
1551 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1552 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1553 }
1554 }
1555 }
1556
1557 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1558 char * at_pos;
1559 int at_offset;
1560 int end_offset;
1561
1562 at_pos = strchr(templates_xten_delim[j], 'X');
1563 at_offset = at_pos - templates_xten_delim[j];
1564 strncpy(buffer, templates_xten_delim[j], at_offset);
1565 buffer[at_offset] = '\0';
1566 strcat(buffer, urls[i].text);
1567 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1568 end_offset = at_offset + strlen(urls[i].text);
1569
1570 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1572
1573 /* This assumes no templates start with the URL itself, and that they
1574 have at least two characters before the URL text */
1575 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1576 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1577 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1578 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1579 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1580 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1581
1582 if (urls[i].is_url)
1583 {
1584 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1585 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1586 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1587 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1588 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1589 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1590 }
1591 else
1592 {
1593 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1594 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1595 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1596 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1597 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1598 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1599 }
1600 if (buffer[end_offset +1] != '\0')
1601 {
1602 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1603 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1604 if (buffer[end_offset +2] != '\0')
1605 {
1606 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1607 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1608 }
1609 }
1610 }
1611
1612 DestroyWindow(hwndRichEdit);
1613 hwndRichEdit = NULL;
1614 }
1615
1616 #define INSERT_CR \
1617 do { \
1618 keybd_event('\r', 0x1c, 0, 0); \
1619 keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
1620 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1621 TranslateMessage(&msg); \
1622 DispatchMessage(&msg); \
1623 } \
1624 } while (0)
1625
1626 #define INSERT_BS \
1627 do { \
1628 keybd_event(0x08, 0x0e, 0, 0); \
1629 keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
1630 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1631 TranslateMessage(&msg); \
1632 DispatchMessage(&msg); \
1633 } \
1634 } while (0)
1635
1636 /* Test detection of URLs within normal text - WM_CHAR case. */
1637 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1638 hwndRichEdit = new_richedit(parent);
1639
1640 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1641 char * at_pos;
1642 int at_offset;
1643 int end_offset;
1644 int u, v;
1645
1646 at_pos = strchr(templates_delim[j], 'X');
1647 at_offset = at_pos - templates_delim[j];
1648 end_offset = at_offset + strlen(urls[i].text);
1649
1650 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1651 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1652 for (u = 0; templates_delim[j][u]; u++) {
1653 if (templates_delim[j][u] == '\r') {
1654 INSERT_CR;
1655 } else if (templates_delim[j][u] != 'X') {
1656 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1657 } else {
1658 for (v = 0; urls[i].text[v]; v++) {
1659 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1660 }
1661 }
1662 }
1663 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1664 trace("Using template: %s\n", templates_delim[j]);
1665
1666 /* This assumes no templates start with the URL itself, and that they
1667 have at least two characters before the URL text */
1668 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1669 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1670 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1671 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1672 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1673 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1674
1675 if (urls[i].is_url)
1676 {
1677 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1678 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1679 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1680 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1681 }
1682 else
1683 {
1684 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1685 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1686 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1687 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1688 }
1689 if (buffer[end_offset] != '\0')
1690 {
1691 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1692 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1693 if (buffer[end_offset +1] != '\0')
1694 {
1695 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1696 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1697 }
1698 }
1699
1700 /* The following will insert a paragraph break after the first character
1701 of the URL candidate, thus breaking the URL. It is expected that the
1702 CFE_LINK attribute should break across both pieces of the URL */
1703 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1704 INSERT_CR;
1705 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1706
1707 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1708 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1711 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1712 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1713
1714 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1715 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1716 /* end_offset moved because of paragraph break */
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1719 if (buffer[end_offset+1] != '\0')
1720 {
1721 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1722 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1723 if (buffer[end_offset +2] != '\0')
1724 {
1725 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1726 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1727 }
1728 }
1729
1730 /* The following will remove the just-inserted paragraph break, thus
1731 restoring the URL */
1732 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1733 INSERT_BS;
1734 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1735
1736 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1737 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1738 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1739 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1740 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1741 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1742
1743 if (urls[i].is_url)
1744 {
1745 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1746 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1747 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1748 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1749 }
1750 else
1751 {
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1756 }
1757 if (buffer[end_offset] != '\0')
1758 {
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1761 if (buffer[end_offset +1] != '\0')
1762 {
1763 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1765 }
1766 }
1767 }
1768 DestroyWindow(hwndRichEdit);
1769 hwndRichEdit = NULL;
1770 }
1771
1772 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1773 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1774 SETTEXTEX st;
1775
1776 hwndRichEdit = new_richedit(parent);
1777
1778 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1779 be detected:
1780 1) Set entire text, a la WM_SETTEXT
1781 2) Set a selection of the text to the URL
1782 3) Set a portion of the text at a time, which eventually results in
1783 an URL
1784 All of them should give equivalent results
1785 */
1786
1787 /* Set entire text in one go, like WM_SETTEXT */
1788 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1789 char * at_pos;
1790 int at_offset;
1791 int end_offset;
1792
1793 st.codepage = CP_ACP;
1794 st.flags = ST_DEFAULT;
1795
1796 at_pos = strchr(templates_delim[j], 'X');
1797 at_offset = at_pos - templates_delim[j];
1798 strncpy(buffer, templates_delim[j], at_offset);
1799 buffer[at_offset] = '\0';
1800 strcat(buffer, urls[i].text);
1801 strcat(buffer, templates_delim[j] + at_offset + 1);
1802 end_offset = at_offset + strlen(urls[i].text);
1803
1804 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1805 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1806
1807 /* This assumes no templates start with the URL itself, and that they
1808 have at least two characters before the URL text */
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1815
1816 if (urls[i].is_url)
1817 {
1818 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1819 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1820 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1821 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1822 }
1823 else
1824 {
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1827 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1828 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1829 }
1830 if (buffer[end_offset] != '\0')
1831 {
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1834 if (buffer[end_offset +1] != '\0')
1835 {
1836 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1837 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1838 }
1839 }
1840 }
1841
1842 /* Set selection with X to the URL */
1843 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1844 char * at_pos;
1845 int at_offset;
1846 int end_offset;
1847
1848 at_pos = strchr(templates_delim[j], 'X');
1849 at_offset = at_pos - templates_delim[j];
1850 end_offset = at_offset + strlen(urls[i].text);
1851
1852 st.codepage = CP_ACP;
1853 st.flags = ST_DEFAULT;
1854 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1855 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1856 st.flags = ST_SELECTION;
1857 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1858 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1859 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1860
1861 /* This assumes no templates start with the URL itself, and that they
1862 have at least two characters before the URL text */
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1865 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1866 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1869
1870 if (urls[i].is_url)
1871 {
1872 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1873 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1874 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1875 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1876 }
1877 else
1878 {
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1883 }
1884 if (buffer[end_offset] != '\0')
1885 {
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1888 if (buffer[end_offset +1] != '\0')
1889 {
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1892 }
1893 }
1894 }
1895
1896 /* Set selection with X to the first character of the URL, then the rest */
1897 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1898 char * at_pos;
1899 int at_offset;
1900 int end_offset;
1901
1902 at_pos = strchr(templates_delim[j], 'X');
1903 at_offset = at_pos - templates_delim[j];
1904 end_offset = at_offset + strlen(urls[i].text);
1905
1906 strcpy(buffer, "YY");
1907 buffer[0] = urls[i].text[0];
1908
1909 st.codepage = CP_ACP;
1910 st.flags = ST_DEFAULT;
1911 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1912 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1913 st.flags = ST_SELECTION;
1914 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1915 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1916 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1917 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1918 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1919
1920 /* This assumes no templates start with the URL itself, and that they
1921 have at least two characters before the URL text */
1922 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1923 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1924 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1925 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1928
1929 if (urls[i].is_url)
1930 {
1931 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1932 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1933 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1934 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1935 }
1936 else
1937 {
1938 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1939 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1942 }
1943 if (buffer[end_offset] != '\0')
1944 {
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1947 if (buffer[end_offset +1] != '\0')
1948 {
1949 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1950 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1951 }
1952 }
1953 }
1954
1955 DestroyWindow(hwndRichEdit);
1956 hwndRichEdit = NULL;
1957 }
1958
1959 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1960 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1961 hwndRichEdit = new_richedit(parent);
1962
1963 /* Set selection with X to the URL */
1964 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1965 char * at_pos;
1966 int at_offset;
1967 int end_offset;
1968
1969 at_pos = strchr(templates_delim[j], 'X');
1970 at_offset = at_pos - templates_delim[j];
1971 end_offset = at_offset + strlen(urls[i].text);
1972
1973 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1975 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1976 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1977 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1978
1979 /* This assumes no templates start with the URL itself, and that they
1980 have at least two characters before the URL text */
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1987
1988 if (urls[i].is_url)
1989 {
1990 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1991 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1992 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1993 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1994 }
1995 else
1996 {
1997 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1998 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2001 }
2002 if (buffer[end_offset] != '\0')
2003 {
2004 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2005 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2006 if (buffer[end_offset +1] != '\0')
2007 {
2008 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2009 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2010 }
2011 }
2012 }
2013
2014 /* Set selection with X to the first character of the URL, then the rest */
2015 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2016 char * at_pos;
2017 int at_offset;
2018 int end_offset;
2019
2020 at_pos = strchr(templates_delim[j], 'X');
2021 at_offset = at_pos - templates_delim[j];
2022 end_offset = at_offset + strlen(urls[i].text);
2023
2024 strcpy(buffer, "YY");
2025 buffer[0] = urls[i].text[0];
2026
2027 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2028 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2029 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2030 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2031 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2033 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2034
2035 /* This assumes no templates start with the URL itself, and that they
2036 have at least two characters before the URL text */
2037 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2038 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2043
2044 if (urls[i].is_url)
2045 {
2046 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2047 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2048 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2049 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2050 }
2051 else
2052 {
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2057 }
2058 if (buffer[end_offset] != '\0')
2059 {
2060 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2061 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2062 if (buffer[end_offset +1] != '\0')
2063 {
2064 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2065 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2066 }
2067 }
2068 }
2069
2070 DestroyWindow(hwndRichEdit);
2071 hwndRichEdit = NULL;
2072 }
2073
2074 DestroyWindow(parent);
2075 }
2076
2077 static void test_EM_SCROLL(void)
2078 {
2079 int i, j;
2080 int r; /* return value */
2081 int expr; /* expected return value */
2082 HWND hwndRichEdit = new_richedit(NULL);
2083 int y_before, y_after; /* units of lines of text */
2084
2085 /* test a richedit box containing a single line of text */
2086 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2087 expr = 0x00010000;
2088 for (i = 0; i < 4; i++) {
2089 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2090
2091 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2092 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2093 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2094 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2095 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2096 "(i == %d)\n", y_after, i);
2097 }
2098
2099 /*
2100 * test a richedit box that will scroll. There are two general
2101 * cases: the case without any long lines and the case with a long
2102 * line.
2103 */
2104 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2105 if (i == 0)
2106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2107 else
2108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2109 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2110 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2111 "LONG LINE \nb\nc\nd\ne");
2112 for (j = 0; j < 12; j++) /* reset scroll position to top */
2113 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2114
2115 /* get first visible line */
2116 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2117 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2118
2119 /* get new current first visible line */
2120 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2121
2122 ok(((r & 0xffffff00) == 0x00010000) &&
2123 ((r & 0x000000ff) != 0x00000000),
2124 "EM_SCROLL page down didn't scroll by a small positive number of "
2125 "lines (r == 0x%08x)\n", r);
2126 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2127 "(line %d scrolled to line %d\n", y_before, y_after);
2128
2129 y_before = y_after;
2130
2131 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2132 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2133 ok(((r & 0xffffff00) == 0x0001ff00),
2134 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2135 "(r == 0x%08x)\n", r);
2136 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2137 "%d scrolled to line %d\n", y_before, y_after);
2138
2139 y_before = y_after;
2140
2141 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2142
2143 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2144
2145 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2146 "(r == 0x%08x)\n", r);
2147 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2148 "1 line (%d scrolled to %d)\n", y_before, y_after);
2149
2150 y_before = y_after;
2151
2152 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2153
2154 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2155
2156 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2157 "(r == 0x%08x)\n", r);
2158 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2159 "line (%d scrolled to %d)\n", y_before, y_after);
2160
2161 y_before = y_after;
2162
2163 r = SendMessage(hwndRichEdit, EM_SCROLL,
2164 SB_LINEUP, 0); /* lineup beyond top */
2165
2166 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2167
2168 ok(r == 0x00010000,
2169 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2170 ok(y_before == y_after,
2171 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2172
2173 y_before = y_after;
2174
2175 r = SendMessage(hwndRichEdit, EM_SCROLL,
2176 SB_PAGEUP, 0);/*page up beyond top */
2177
2178 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2179
2180 ok(r == 0x00010000,
2181 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2182 ok(y_before == y_after,
2183 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2184
2185 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2186 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2187 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2188 r = SendMessage(hwndRichEdit, EM_SCROLL,
2189 SB_PAGEDOWN, 0); /* page down beyond bot */
2190 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2191
2192 ok(r == 0x00010000,
2193 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2194 ok(y_before == y_after,
2195 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2196 y_before, y_after);
2197
2198 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2199 SendMessage(hwndRichEdit, EM_SCROLL,
2200 SB_LINEDOWN, 0); /* line down beyond bot */
2201 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2202
2203 ok(r == 0x00010000,
2204 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2205 ok(y_before == y_after,
2206 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2207 y_before, y_after);
2208 }
2209 DestroyWindow(hwndRichEdit);
2210 }
2211
2212 static void test_EM_SETUNDOLIMIT(void)
2213 {
2214 /* cases we test for:
2215 * default behaviour - limiting at 100 undo's
2216 * undo disabled - setting a limit of 0
2217 * undo limited - undo limit set to some to some number, like 2
2218 * bad input - sending a negative number should default to 100 undo's */
2219
2220 HWND hwndRichEdit = new_richedit(NULL);
2221 CHARRANGE cr;
2222 int i;
2223 int result;
2224
2225 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2226 cr.cpMin = 0;
2227 cr.cpMax = 1;
2228 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2229 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2230 also, multiple pastes don't combine like WM_CHAR would */
2231 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2232
2233 /* first case - check the default */
2234 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2235 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2236 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2237 for (i=0; i<100; i++) /* Undo 100 of them */
2238 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2239 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2240 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2241
2242 /* second case - cannot undo */
2243 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2244 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2245 SendMessage(hwndRichEdit,
2246 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2247 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2248 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2249
2250 /* third case - set it to an arbitrary number */
2251 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2252 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2253 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2254 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2255 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2256 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2257 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2258 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2259 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2260 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2261 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2262 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2263 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2264 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2265
2266 /* fourth case - setting negative numbers should default to 100 undos */
2267 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2268 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2269 ok (result == 100,
2270 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2271
2272 DestroyWindow(hwndRichEdit);
2273 }
2274
2275 static void test_ES_PASSWORD(void)
2276 {
2277 /* This isn't hugely testable, so we're just going to run it through its paces */
2278
2279 HWND hwndRichEdit = new_richedit(NULL);
2280 WCHAR result;
2281
2282 /* First, check the default of a regular control */
2283 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2284 ok (result == 0,
2285 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2286
2287 /* Now, set it to something normal */
2288 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2289 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2290 ok (result == 120,
2291 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2292
2293 /* Now, set it to something odd */
2294 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2295 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2296 ok (result == 1234,
2297 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2298 DestroyWindow(hwndRichEdit);
2299 }
2300
2301 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2302 LPBYTE pbBuff,
2303 LONG cb,
2304 LONG *pcb)
2305 {
2306 char** str = (char**)dwCookie;
2307 *pcb = cb;
2308 if (*pcb > 0) {
2309 memcpy(*str, pbBuff, *pcb);
2310 *str += *pcb;
2311 }
2312 return 0;
2313 }
2314
2315 static void test_WM_SETTEXT()
2316 {
2317 HWND hwndRichEdit = new_richedit(NULL);
2318 const char * TestItem1 = "TestSomeText";
2319 const char * TestItem2 = "TestSomeText\r";
2320 const char * TestItem2_after = "TestSomeText\r\n";
2321 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2322 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2323 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2324 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2325 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2326 const char * TestItem5_after = "TestSomeText TestSomeText";
2327 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2328 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2329 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2330 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2331
2332 char buf[1024] = {0};
2333 LRESULT result;
2334 EDITSTREAM es;
2335 char * p;
2336
2337 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2338 any solitary \r to be converted to \r\n on return. Properly paired
2339 \r\n are not affected. It also shows that the special sequence \r\r\n
2340 gets converted to a single space.
2341 */
2342
2343 #define TEST_SETTEXT(a, b) \
2344 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2345 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2346 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2347 ok (result == lstrlen(buf), \
2348 "WM_GETTEXT returned %ld instead of expected %u\n", \
2349 result, lstrlen(buf)); \
2350 result = strcmp(b, buf); \
2351 ok(result == 0, \
2352 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2353
2354 TEST_SETTEXT(TestItem1, TestItem1)
2355 TEST_SETTEXT(TestItem2, TestItem2_after)
2356 TEST_SETTEXT(TestItem3, TestItem3_after)
2357 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2358 TEST_SETTEXT(TestItem4, TestItem4_after)
2359 TEST_SETTEXT(TestItem5, TestItem5_after)
2360 TEST_SETTEXT(TestItem6, TestItem6_after)
2361 TEST_SETTEXT(TestItem7, TestItem7_after)
2362
2363 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2365 p = buf;
2366 es.dwCookie = (DWORD_PTR)&p;
2367 es.dwError = 0;
2368 es.pfnCallback = test_WM_SETTEXT_esCallback;
2369 memset(buf, 0, sizeof(buf));
2370 SendMessage(hwndRichEdit, EM_STREAMOUT,
2371 (WPARAM)(SF_RTF), (LPARAM)&es);
2372 trace("EM_STREAMOUT produced: \n%s\n", buf);
2373 TEST_SETTEXT(buf, TestItem1)
2374
2375 #undef TEST_SETTEXT
2376 DestroyWindow(hwndRichEdit);
2377 }
2378
2379 static void test_EM_STREAMOUT(void)
2380 {
2381 HWND hwndRichEdit = new_richedit(NULL);
2382 int r;
2383 EDITSTREAM es;
2384 char buf[1024] = {0};
2385 char * p;
2386
2387 const char * TestItem1 = "TestSomeText";
2388 const char * TestItem2 = "TestSomeText\r";
2389 const char * TestItem3 = "TestSomeText\r\n";
2390
2391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2392 p = buf;
2393 es.dwCookie = (DWORD_PTR)&p;
2394 es.dwError = 0;
2395 es.pfnCallback = test_WM_SETTEXT_esCallback;
2396 memset(buf, 0, sizeof(buf));
2397 SendMessage(hwndRichEdit, EM_STREAMOUT,
2398 (WPARAM)(SF_TEXT), (LPARAM)&es);
2399 r = strlen(buf);
2400 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2401 ok(strcmp(buf, TestItem1) == 0,
2402 "streamed text different, got %s\n", buf);
2403
2404 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2405 p = buf;
2406 es.dwCookie = (DWORD_PTR)&p;
2407 es.dwError = 0;
2408 es.pfnCallback = test_WM_SETTEXT_esCallback;
2409 memset(buf, 0, sizeof(buf));
2410 SendMessage(hwndRichEdit, EM_STREAMOUT,
2411 (WPARAM)(SF_TEXT), (LPARAM)&es);
2412 r = strlen(buf);
2413 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2414 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2415 ok(strcmp(buf, TestItem3) == 0,
2416 "streamed text different from, got %s\n", buf);
2417 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2418 p = buf;
2419 es.dwCookie = (DWORD_PTR)&p;
2420 es.dwError = 0;
2421 es.pfnCallback = test_WM_SETTEXT_esCallback;
2422 memset(buf, 0, sizeof(buf));
2423 SendMessage(hwndRichEdit, EM_STREAMOUT,
2424 (WPARAM)(SF_TEXT), (LPARAM)&es);
2425 r = strlen(buf);
2426 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2427 ok(strcmp(buf, TestItem3) == 0,
2428 "streamed text different, got %s\n", buf);
2429
2430 DestroyWindow(hwndRichEdit);
2431 }
2432
2433 static void test_EM_SETTEXTEX(void)
2434 {
2435 HWND hwndRichEdit = new_richedit(NULL);
2436 SETTEXTEX setText;
2437 GETTEXTEX getText;
2438 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2439 'S', 'o', 'm', 'e',
2440 'T', 'e', 'x', 't', 0};
2441 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2442 'S', 'o', 'm', 'e',
2443 'T', 'e', 'x', 't',
2444 '\r', 0};
2445 const char * TestItem2_after = "TestSomeText\r\n";
2446 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2447 'S', 'o', 'm', 'e',
2448 'T', 'e', 'x', 't',
2449 '\r','\n','\r','\n', 0};
2450 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2451 'S', 'o', 'm', 'e',
2452 'T', 'e', 'x', 't',
2453 '\n','\n', 0};
2454 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2455 'S', 'o', 'm', 'e',
2456 'T', 'e', 'x', 't',
2457 '\r','\r', 0};
2458 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2459 'S', 'o', 'm', 'e',
2460 'T', 'e', 'x', 't',
2461 '\r','\r','\n','\r',
2462 '\n', 0};
2463 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2464 'S', 'o', 'm', 'e',
2465 'T', 'e', 'x', 't',
2466 ' ','\r', 0};
2467 #define MAX_BUF_LEN 1024
2468 WCHAR buf[MAX_BUF_LEN];
2469 char * p;
2470 int result;
2471 CHARRANGE cr;
2472 EDITSTREAM es;
2473
2474 setText.codepage = 1200; /* no constant for unicode */
2475 getText.codepage = 1200; /* no constant for unicode */
2476 getText.cb = MAX_BUF_LEN;
2477 getText.flags = GT_DEFAULT;
2478 getText.lpDefaultChar = NULL;
2479 getText.lpUsedDefChar = NULL;
2480
2481 setText.flags = 0;
2482 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2483 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2484 ok(lstrcmpW(buf, TestItem1) == 0,
2485 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2486
2487 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2488 convert \r to \r\n on return
2489 */
2490 setText.codepage = 1200; /* no constant for unicode */
2491 getText.codepage = 1200; /* no constant for unicode */
2492 getText.cb = MAX_BUF_LEN;
2493 getText.flags = GT_DEFAULT;
2494 getText.lpDefaultChar = NULL;
2495 getText.lpUsedDefChar = NULL;
2496 setText.flags = 0;
2497 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2498 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2499 ok(lstrcmpW(buf, TestItem2) == 0,
2500 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2501
2502 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2503 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2504 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2505 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2506
2507 /* Baseline test for just-enough buffer space for string */
2508 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2509 getText.codepage = 1200; /* no constant for unicode */
2510 getText.flags = GT_DEFAULT;
2511 getText.lpDefaultChar = NULL;
2512 getText.lpUsedDefChar = NULL;
2513 memset(buf, 0, MAX_BUF_LEN);
2514 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2515 ok(lstrcmpW(buf, TestItem2) == 0,
2516 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2517
2518 /* When there is enough space for one character, but not both, of the CRLF
2519 pair at the end of the string, the CR is not copied at all. That is,
2520 the caller must not see CRLF pairs truncated to CR at the end of the
2521 string.
2522 */
2523 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2524 getText.codepage = 1200; /* no constant for unicode */
2525 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2526 getText.lpDefaultChar = NULL;
2527 getText.lpUsedDefChar = NULL;
2528 memset(buf, 0, MAX_BUF_LEN);
2529 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2530 ok(lstrcmpW(buf, TestItem1) == 0,
2531 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2532
2533
2534 /* \r\n pairs get changed into \r */
2535 setText.codepage = 1200; /* no constant for unicode */
2536 getText.codepage = 1200; /* no constant for unicode */
2537 getText.cb = MAX_BUF_LEN;
2538 getText.flags = GT_DEFAULT;
2539 getText.lpDefaultChar = NULL;
2540 getText.lpUsedDefChar = NULL;
2541 setText.flags = 0;
2542 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2544 ok(lstrcmpW(buf, TestItem3_after) == 0,
2545 "EM_SETTEXTEX did not convert properly\n");
2546
2547 /* \n also gets changed to \r */
2548 setText.codepage = 1200; /* no constant for unicode */
2549 getText.codepage = 1200; /* no constant for unicode */
2550 getText.cb = MAX_BUF_LEN;
2551 getText.flags = GT_DEFAULT;
2552 getText.lpDefaultChar = NULL;
2553 getText.lpUsedDefChar = NULL;
2554 setText.flags = 0;
2555 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2556 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2557 ok(lstrcmpW(buf, TestItem3_after) == 0,
2558 "EM_SETTEXTEX did not convert properly\n");
2559
2560 /* \r\r\n gets changed into single space */
2561 setText.codepage = 1200; /* no constant for unicode */
2562 getText.codepage = 1200; /* no constant for unicode */
2563 getText.cb = MAX_BUF_LEN;
2564 getText.flags = GT_DEFAULT;
2565 getText.lpDefaultChar = NULL;
2566 getText.lpUsedDefChar = NULL;
2567 setText.flags = 0;
2568 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2569 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2570 ok(lstrcmpW(buf, TestItem4_after) == 0,
2571 "EM_SETTEXTEX did not convert properly\n");
2572
2573 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2574 (WPARAM)&setText, (LPARAM) NULL);
2575 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2576
2577 ok (result == 1,
2578 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2579 ok(lstrlenW(buf) == 0,
2580 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2581
2582 /* put some text back */
2583 setText.flags = 0;
2584 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2585 /* select some text */
2586 cr.cpMax = 1;
2587 cr.cpMin = 3;
2588 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2589 /* replace current selection */
2590 setText.flags = ST_SELECTION;
2591 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2592 (WPARAM)&setText, (LPARAM) NULL);
2593 ok(result == 0,
2594 "EM_SETTEXTEX with NULL lParam to replace selection"
2595 " with no text should return 0. Got %i\n",
2596 result);
2597
2598 /* put some text back */
2599 setText.flags = 0;
2600 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2601 /* select some text */
2602 cr.cpMax = 1;
2603 cr.cpMin = 3;
2604 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2605 /* replace current selection */
2606 setText.flags = ST_SELECTION;
2607 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2608 (WPARAM)&setText, (LPARAM) TestItem1);
2609 /* get text */
2610 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2611 ok(result == lstrlenW(TestItem1),
2612 "EM_SETTEXTEX with NULL lParam to replace selection"
2613 " with no text should return 0. Got %i\n",
2614 result);
2615 ok(lstrlenW(buf) == 22,
2616 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2617 lstrlenW(buf) );
2618
2619 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2620 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2621 p = (char *)buf;
2622 es.dwCookie = (DWORD_PTR)&p;
2623 es.dwError = 0;
2624 es.pfnCallback = test_WM_SETTEXT_esCallback;
2625 memset(buf, 0, sizeof(buf));
2626 SendMessage(hwndRichEdit, EM_STREAMOUT,
2627 (WPARAM)(SF_RTF), (LPARAM)&es);
2628 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2629
2630 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2631 getText.codepage = 1200; /* no constant for unicode */
2632 getText.cb = MAX_BUF_LEN;
2633 getText.flags = GT_DEFAULT;
2634 getText.lpDefaultChar = NULL;
2635 getText.lpUsedDefChar = NULL;
2636
2637 setText.flags = 0;
2638 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2639 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2640 ok(lstrcmpW(buf, TestItem1) == 0,
2641 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2642
2643
2644 DestroyWindow(hwndRichEdit);
2645 }
2646
2647 static void test_EM_LIMITTEXT(void)
2648 {
2649 int ret;
2650
2651 HWND hwndRichEdit = new_richedit(NULL);
2652
2653 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2654 * about setting the length to -1 for multiline edit controls doesn't happen.
2655 */
2656
2657 /* Don't check default gettextlimit case. That's done in other tests */
2658
2659 /* Set textlimit to 100 */
2660 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2661 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2662 ok (ret == 100,
2663 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2664
2665 /* Set textlimit to 0 */
2666 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2667 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2668 ok (ret == 65536,
2669 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2670
2671 /* Set textlimit to -1 */
2672 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2673 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2674 ok (ret == -1,
2675 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2676
2677 /* Set textlimit to -2 */
2678 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2679 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2680 ok (ret == -2,
2681 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2682
2683 DestroyWindow (hwndRichEdit);
2684 }
2685
2686
2687 static void test_EM_EXLIMITTEXT(void)
2688 {
2689 int i, selBegin, selEnd, len1, len2;
2690 int result;
2691 char text[1024 + 1];
2692 char buffer[1024 + 1];
2693 int textlimit = 0; /* multiple of 100 */
2694 HWND hwndRichEdit = new_richedit(NULL);
2695
2696 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2697 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2698
2699 textlimit = 256000;
2700 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2701 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2702 /* set higher */
2703 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2704
2705 textlimit = 1000;
2706 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2707 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2708 /* set lower */
2709 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2710
2711 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2712 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2713 /* default for WParam = 0 */
2714 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2715
2716 textlimit = sizeof(text)-1;
2717 memset(text, 'W', textlimit);
2718 text[sizeof(text)-1] = 0;
2719 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2720 /* maxed out text */
2721 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2722
2723 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2724 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2725 len1 = selEnd - selBegin;
2726
2727 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2728 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2729 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2730 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2731 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2732 len2 = selEnd - selBegin;
2733
2734 ok(len1 != len2,
2735 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2736 len1,len2,i);
2737
2738 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2739 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2740 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2741 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2742 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2743 len1 = selEnd - selBegin;
2744
2745 ok(len1 != len2,
2746 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2747 len1,len2,i);
2748
2749 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2750 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2751 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2752 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2753 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2754 len2 = selEnd - selBegin;
2755
2756 ok(len1 == len2,
2757 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2758 len1,len2,i);
2759
2760 /* set text up to the limit, select all the text, then add a char */
2761 textlimit = 5;
2762 memset(text, 'W', textlimit);
2763 text[textlimit] = 0;
2764 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2766 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2767 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2768 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2769 result = strcmp(buffer, "A");
2770 ok(0 == result, "got string = \"%s\"\n", buffer);
2771
2772 /* WM_SETTEXT not limited */
2773 textlimit = 10;
2774 memset(text, 'W', textlimit);
2775 text[textlimit] = 0;
2776 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2778 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2779 i = strlen(buffer);
2780 ok(10 == i, "expected 10 chars\n");
2781 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2782 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2783
2784 /* try inserting more text at end */
2785 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2786 ok(0 == i, "WM_CHAR wasn't processed\n");
2787 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2788 i = strlen(buffer);
2789 ok(10 == i, "expected 10 chars, got %i\n", i);
2790 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2791 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2792
2793 /* try inserting text at beginning */
2794 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2795 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2796 ok(0 == i, "WM_CHAR wasn't processed\n");
2797 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2798 i = strlen(buffer);
2799 ok(10 == i, "expected 10 chars, got %i\n", i);
2800 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2801 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2802
2803 /* WM_CHAR is limited */
2804 textlimit = 1;
2805 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2806 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2807 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2808 ok(0 == i, "WM_CHAR wasn't processed\n");
2809 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2810 ok(0 == i, "WM_CHAR wasn't processed\n");
2811 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2812 i = strlen(buffer);
2813 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2814
2815 DestroyWindow(hwndRichEdit);
2816 }
2817
2818 static void test_EM_GETLIMITTEXT(void)
2819 {
2820 int i;
2821 HWND hwndRichEdit = new_richedit(NULL);
2822
2823 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2824 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2825
2826 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2827 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2828 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2829
2830 DestroyWindow(hwndRichEdit);
2831 }
2832
2833 static void test_WM_SETFONT(void)
2834 {
2835 /* There is no invalid input or error conditions for this function.
2836 * NULL wParam and lParam just fall back to their default values
2837 * It should be noted that even if you use a gibberish name for your fonts
2838 * here, it will still work because the name is stored. They will display as
2839 * System, but will report their name to be whatever they were created as */
2840
2841 HWND hwndRichEdit = new_richedit(NULL);
2842 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2843 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2844 FF_DONTCARE, "Marlett");
2845 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2846 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2847 FF_DONTCARE, "MS Sans Serif");
2848 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2849 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2850 FF_DONTCARE, "Courier");
2851 LOGFONTA sentLogFont;
2852 CHARFORMAT2A returnedCF2A;
2853
2854 returnedCF2A.cbSize = sizeof(returnedCF2A);
2855
2856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2857 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2858 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2859
2860 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2861 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2862 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2863 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2864
2865 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2866 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2867 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2868 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2869 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2870 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2871
2872 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2873 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2874 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2875 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2876 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2877 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2878
2879 /* This last test is special since we send in NULL. We clear the variables
2880 * and just compare to "System" instead of the sent in font name. */
2881 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2882 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2883 returnedCF2A.cbSize = sizeof(returnedCF2A);
2884
2885 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2886 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2887 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2888 ok (!strcmp("System",returnedCF2A.szFaceName),
2889 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2890
2891 DestroyWindow(hwndRichEdit);
2892 }
2893
2894
2895 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2896 LPBYTE pbBuff,
2897 LONG cb,
2898 LONG *pcb)
2899 {
2900 const char** str = (const char**)dwCookie;
2901 int size = strlen(*str);
2902 if(size > 3) /* let's make it piecemeal for fun */
2903 size = 3;
2904 *pcb = cb;
2905 if (*pcb > size) {
2906 *pcb = size;
2907 }
2908 if (*pcb > 0) {
2909 memcpy(pbBuff, *str, *pcb);
2910 *str += *pcb;
2911 }
2912 return 0;
2913 }
2914
2915 static void test_EM_GETMODIFY(void)
2916 {
2917 HWND hwndRichEdit = new_richedit(NULL);
2918 LRESULT result;
2919 SETTEXTEX setText;
2920 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2921 'S', 'o', 'm', 'e',
2922 'T', 'e', 'x', 't', 0};
2923 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2924 'S', 'o', 'm', 'e',
2925 'O', 't', 'h', 'e', 'r',
2926 'T', 'e', 'x', 't', 0};
2927 const char* streamText = "hello world";
2928 CHARFORMAT2 cf2;
2929 PARAFORMAT2 pf2;
2930 EDITSTREAM es;
2931
2932 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2933 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2934 FF_DONTCARE, "Courier");
2935
2936 setText.codepage = 1200; /* no constant for unicode */
2937 setText.flags = ST_KEEPUNDO;
2938
2939
2940 /* modify flag shouldn't be set when richedit is first created */
2941 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2942 ok (result == 0,
2943 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2944
2945 /* setting modify flag should actually set it */
2946 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2947 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2948 ok (result != 0,
2949 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2950
2951 /* clearing modify flag should actually clear it */
2952 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2953 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2954 ok (result == 0,
2955 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2956
2957 /* setting font doesn't change modify flag */
2958 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2959 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2960 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2961 ok (result == 0,
2962 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2963
2964 /* setting text should set modify flag */
2965 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2966 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2967 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2968 ok (result != 0,
2969 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2970
2971 /* undo previous text doesn't reset modify flag */
2972 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2973 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2974 ok (result != 0,
2975 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2976
2977 /* set text with no flag to keep undo stack should not set modify flag */
2978 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2979 setText.flags = 0;
2980 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2981 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982 ok (result == 0,
2983 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2984
2985 /* WM_SETTEXT doesn't modify */
2986 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2987 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2988 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2989 ok (result == 0,
2990 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2991
2992 /* clear the text */
2993 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2994 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2995 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2996 ok (result == 0,
2997 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2998
2999 /* replace text */
3000 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3001 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3002 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3003 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3004 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3005 ok (result != 0,
3006 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3007
3008 /* copy/paste text 1 */
3009 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3010 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3011 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3012 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3013 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);