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