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