2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
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.
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.
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
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit
;
37 static HWND
new_window(LPCTSTR lpClassName
, DWORD dwStyle
, HWND parent
) {
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());
46 static HWND
new_richedit(HWND parent
) {
47 return new_window(RICHEDIT_CLASS
, ES_MULTILINE
, parent
);
50 static const char haystack
[] = "WINEWine wineWine wine WineWine";
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}
72 struct find_s find_tests2
[] = {
74 {0, -1, "foo", FR_DOWN
| FR_MATCHCASE
, -1, 0},
75 {5, 20, "WINE", FR_DOWN
| FR_MATCHCASE
, -1, 0},
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},
84 {19, 20, "Wine", FR_MATCHCASE
, 13, 0},
85 {10, 20, "Wine", FR_MATCHCASE
, 4, 0},
86 {20, 10, "Wine", FR_MATCHCASE
, 13, 0},
88 /* Case-insensitive */
89 {1, 31, "wInE", FR_DOWN
, 4, 0},
90 {1, 31, "Wine", FR_DOWN
, 4, 0},
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},
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},
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},
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},
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},
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}
133 static void check_EM_FINDTEXT(HWND hwnd
, const char *name
, struct find_s
*f
, int id
) {
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
);
146 static void check_EM_FINDTEXTEX(HWND hwnd
, const char *name
, struct find_s
*f
,
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
);
167 static void run_tests_EM_FINDTEXT(HWND hwnd
, const char *name
, struct find_s
*find
,
172 for (i
= 0; i
< num_tests
; i
++) {
173 if (find
[i
]._todo_wine
) {
175 check_EM_FINDTEXT(hwnd
, name
, &find
[i
], i
);
176 check_EM_FINDTEXTEX(hwnd
, name
, &find
[i
], i
);
179 check_EM_FINDTEXT(hwnd
, name
, &find
[i
], i
);
180 check_EM_FINDTEXTEX(hwnd
, name
, &find
[i
], i
);
185 static void test_EM_FINDTEXT(void)
187 HWND hwndRichEdit
= new_richedit(NULL
);
189 /* Empty rich edit control */
190 run_tests_EM_FINDTEXT(hwndRichEdit
, "1", find_tests
,
191 sizeof(find_tests
)/sizeof(struct find_s
));
193 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) haystack
);
196 run_tests_EM_FINDTEXT(hwndRichEdit
, "2", find_tests2
,
197 sizeof(find_tests2
)/sizeof(struct find_s
));
199 DestroyWindow(hwndRichEdit
);
202 static const struct getline_s
{
207 {0, 10, "foo bar\r"},
212 /* Buffer smaller than line length */
218 static void test_EM_GETLINE(void)
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"
228 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
230 memset(origdest
, 0xBB, nBuf
);
231 for (i
= 0; i
< sizeof(gl
)/sizeof(struct getline_s
); i
++)
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
;
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
,
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),
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");
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
);
261 DestroyWindow(hwndRichEdit
);
264 static int get_scroll_pos_y(HWND hwnd
)
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
);
272 static void move_cursor(HWND hwnd
, long charindex
)
275 cr
.cpMax
= charindex
;
276 cr
.cpMin
= charindex
;
277 SendMessage(hwnd
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
280 static void line_scroll(HWND hwnd
, int amount
)
282 SendMessage(hwnd
, EM_LINESCROLL
, 0, amount
);
285 static void test_EM_SCROLLCARET(void)
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 "
299 /* Can't verify this */
300 SendMessage(hwndRichEdit
, EM_SCROLLCARET
, 0, 0);
302 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
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
);
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
);
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
);
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
);
333 DestroyWindow(hwndRichEdit
);
336 static void test_EM_SETTEXTMODE(void)
338 HWND hwndRichEdit
= new_richedit(NULL
);
339 CHARFORMAT2 cf2
, cf2test
;
343 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
344 /*Insert text into the control*/
346 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
348 /*Attempt to change the control to plain text mode*/
349 rc
= SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, (WPARAM
) TM_PLAINTEXT
, 0);
350 ok(rc
!= 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc
);
352 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
353 If rich text is pasted, it should have the same formatting as the rest
354 of the text in the control*/
357 *NOTE: If the default text was already italicized, the test will simply
358 reverse; in other words, it will copy a regular "wine" into a plain
359 text window that uses an italicized format*/
360 cf2
.cbSize
= sizeof(CHARFORMAT2
);
361 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_DEFAULT
,
364 cf2
.dwMask
= CFM_ITALIC
| cf2
.dwMask
;
365 cf2
.dwEffects
= CFE_ITALIC
^ cf2
.dwEffects
;
367 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
368 however, SCF_ALL has been implemented*/
369 SendMessage(hwndRichEdit
, EM_SETCHARFORMAT
, (WPARAM
) SCF_ALL
, (LPARAM
) &cf2
);
370 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
372 /*Select the string "wine"*/
375 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
377 /*Copy the italicized "wine" to the clipboard*/
378 SendMessage(hwndRichEdit
, WM_COPY
, 0, 0);
380 /*Reset the formatting to default*/
381 cf2
.dwEffects
= CFE_ITALIC
^cf2
.dwEffects
;
382 SendMessage(hwndRichEdit
, EM_SETCHARFORMAT
, (WPARAM
) SCF_ALL
, (LPARAM
) &cf2
);
384 /*Clear the text in the control*/
385 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "");
387 /*Switch to Plain Text Mode*/
388 rc
= SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, (WPARAM
) TM_PLAINTEXT
, 0);
389 ok(rc
== 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc
);
391 /*Input "wine" again in normal format*/
392 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
394 /*Paste the italicized "wine" into the control*/
395 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
397 /*Select a character from the first "wine" string*/
400 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
402 /*Retrieve its formatting*/
403 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
,
406 /*Select a character from the second "wine" string*/
409 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
411 /*Retrieve its formatting*/
412 cf2test
.cbSize
= sizeof(CHARFORMAT2
);
413 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
,
416 /*Compare the two formattings*/
417 ok((cf2
.dwMask
== cf2test
.dwMask
) && (cf2
.dwEffects
== cf2test
.dwEffects
),
418 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
419 cf2
.dwEffects
, cf2test
.dwEffects
);
420 /*Test TM_RICHTEXT by: switching back to Rich Text mode
421 printing "wine" in the current format(normal)
422 pasting "wine" from the clipboard(italicized)
423 comparing the two formats(should differ)*/
425 /*Attempt to switch with text in control*/
426 rc
= SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, (WPARAM
) TM_RICHTEXT
, 0);
427 ok(rc
!= 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc
);
430 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "");
432 /*Switch into Rich Text mode*/
433 rc
= SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, (WPARAM
) TM_RICHTEXT
, 0);
434 ok(rc
== 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc
);
436 /*Print "wine" in normal formatting into the control*/
437 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
439 /*Paste italicized "wine" into the control*/
440 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
442 /*Select text from the first "wine" string*/
445 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
447 /*Retrieve its formatting*/
448 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
,
451 /*Select text from the second "wine" string*/
454 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
456 /*Retrieve its formatting*/
457 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
,
460 /*Test that the two formattings are not the same*/
461 todo_wine
ok((cf2
.dwMask
== cf2test
.dwMask
) && (cf2
.dwEffects
!= cf2test
.dwEffects
),
462 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
463 cf2
.dwMask
, cf2test
.dwMask
, cf2
.dwEffects
, cf2test
.dwEffects
);
465 DestroyWindow(hwndRichEdit
);
468 static void test_TM_PLAINTEXT(void)
470 /*Tests plain text properties*/
472 HWND hwndRichEdit
= new_richedit(NULL
);
473 CHARFORMAT2 cf2
, cf2test
;
476 /*Switch to plain text mode*/
478 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "");
479 SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, TM_PLAINTEXT
, 0);
481 /*Fill control with text*/
483 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "Is Wine an emulator? No it's not");
485 /*Select some text and bold it*/
489 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
490 cf2
.cbSize
= sizeof(CHARFORMAT2
);
491 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_DEFAULT
,
494 cf2
.dwMask
= CFM_BOLD
| cf2
.dwMask
;
495 cf2
.dwEffects
= CFE_BOLD
^ cf2
.dwEffects
;
497 SendMessage(hwndRichEdit
, EM_SETCHARFORMAT
, (WPARAM
) SCF_SELECTION
, (LPARAM
) &cf2
);
499 /*Get the formatting of those characters*/
501 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
, (LPARAM
) &cf2
);
503 /*Get the formatting of some other characters*/
504 cf2test
.cbSize
= sizeof(CHARFORMAT2
);
507 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
508 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
, (LPARAM
) &cf2test
);
510 /*Test that they are the same as plain text allows only one formatting*/
512 ok((cf2
.dwMask
== cf2test
.dwMask
) && (cf2
.dwEffects
== cf2test
.dwEffects
),
513 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
514 cf2
.dwMask
, cf2test
.dwMask
, cf2
.dwEffects
, cf2test
.dwEffects
);
516 /*Fill the control with a "wine" string, which when inserted will be bold*/
518 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
520 /*Copy the bolded "wine" string*/
524 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
525 SendMessage(hwndRichEdit
, WM_COPY
, 0, 0);
527 /*Swap back to rich text*/
529 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "");
530 SendMessage(hwndRichEdit
, EM_SETTEXTMODE
, (WPARAM
) TM_RICHTEXT
, 0);
532 /*Set the default formatting to bold italics*/
534 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_DEFAULT
, (LPARAM
) &cf2
);
535 cf2
.dwMask
|= CFM_ITALIC
;
536 cf2
.dwEffects
^= CFE_ITALIC
;
537 SendMessage(hwndRichEdit
, EM_SETCHARFORMAT
, (WPARAM
) SCF_ALL
, (LPARAM
) &cf2
);
539 /*Set the text in the control to "wine", which will be bold and italicized*/
541 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "wine");
543 /*Paste the plain text "wine" string, which should take the insert
544 formatting, which at the moment is bold italics*/
546 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
548 /*Select the first "wine" string and retrieve its formatting*/
552 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
553 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
, (LPARAM
) &cf2
);
555 /*Select the second "wine" string and retrieve its formatting*/
559 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
560 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_SELECTION
, (LPARAM
) &cf2test
);
562 /*Compare the two formattings. They should be the same.*/
564 ok((cf2
.dwMask
== cf2test
.dwMask
) && (cf2
.dwEffects
== cf2test
.dwEffects
),
565 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
566 cf2
.dwMask
, cf2test
.dwMask
, cf2
.dwEffects
, cf2test
.dwEffects
);
567 DestroyWindow(hwndRichEdit
);
570 static void test_WM_GETTEXT(void)
572 HWND hwndRichEdit
= new_richedit(NULL
);
573 static const char text
[] = "Hello. My name is RichEdit!";
574 char buffer
[1024] = {0};
577 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
578 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
579 result
= strcmp(buffer
,text
);
581 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result
);
582 DestroyWindow(hwndRichEdit
);
585 /* FIXME: need to test unimplemented options and robustly test wparam */
586 static void test_EM_SETOPTIONS(void)
588 HWND hwndRichEdit
= new_richedit(NULL
);
589 static const char text
[] = "Hello. My name is RichEdit!";
590 char buffer
[1024] = {0};
592 /* NEGATIVE TESTING - NO OPTIONS SET */
593 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
594 SendMessage(hwndRichEdit
, EM_SETOPTIONS
, ECOOP_SET
, 0);
596 /* testing no readonly by sending 'a' to the control*/
597 SetFocus(hwndRichEdit
);
598 SendMessage(hwndRichEdit
, WM_CHAR
, 'a', 0x1E0001);
599 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
601 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text
, buffer
);
602 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
604 /* READONLY - sending 'a' to the control */
605 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
606 SendMessage(hwndRichEdit
, EM_SETOPTIONS
, ECOOP_SET
, ECO_READONLY
);
607 SetFocus(hwndRichEdit
);
608 SendMessage(hwndRichEdit
, WM_CHAR
, 'a', 0x1E0001);
609 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
610 ok(buffer
[0]==text
[0],
611 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text
, buffer
);
613 DestroyWindow(hwndRichEdit
);
616 static void check_CFE_LINK_rcvd(HWND hwnd
, int is_url
)
618 CHARFORMAT2W text_format
;
619 int link_present
= 0;
620 text_format
.cbSize
= sizeof(text_format
);
621 SendMessage(hwnd
, EM_SETSEL
, 0, 0);
622 SendMessage(hwnd
, EM_GETCHARFORMAT
, SCF_SELECTION
, (LPARAM
) &text_format
);
623 link_present
= text_format
.dwEffects
& CFE_LINK
;
625 { /* control text is url; should get CFE_LINK */
626 ok(0 != link_present
, "URL Case: CFE_LINK not set.\n");
630 ok(0 == link_present
, "Non-URL Case: CFE_LINK set.\n");
634 static HWND
new_static_wnd(HWND parent
) {
635 return new_window("Static", 0, parent
);
638 static void test_EM_AUTOURLDETECT(void)
645 {"http://www.winehq.org", 1},
646 {"http//winehq.org", 0},
647 {"ww.winehq.org", 0},
648 {"www.winehq.org", 1},
649 {"ftp://192.168.1.1", 1},
650 {"ftp//192.168.1.1", 0},
651 {"mailto:your@email.com", 1},
652 {"prospero:prosperoserver", 1},
654 {"news:newserver", 1},
655 {"wais:waisserver", 1}
660 HWND hwndRichEdit
, parent
;
662 parent
= new_static_wnd(NULL
);
663 hwndRichEdit
= new_richedit(parent
);
664 /* Try and pass EM_AUTOURLDETECT some test wParam values */
665 urlRet
=SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, FALSE
, 0);
666 ok(urlRet
==0, "Good wParam: urlRet is: %d\n", urlRet
);
667 urlRet
=SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, 1, 0);
668 ok(urlRet
==0, "Good wParam2: urlRet is: %d\n", urlRet
);
669 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
670 urlRet
=SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, 8, 0);
671 ok(urlRet
==E_INVALIDARG
, "Bad wParam: urlRet is: %d\n", urlRet
);
672 urlRet
=SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, (WPARAM
)"h", (LPARAM
)"h");
673 ok(urlRet
==E_INVALIDARG
, "Bad wParam2: urlRet is: %d\n", urlRet
);
674 /* for each url, check the text to see if CFE_LINK effect is present */
675 for (i
= 0; i
< sizeof(urls
)/sizeof(struct urls_s
); i
++) {
676 SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, FALSE
, 0);
677 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) urls
[i
].text
);
678 SendMessage(hwndRichEdit
, WM_CHAR
, 0, 0);
679 check_CFE_LINK_rcvd(hwndRichEdit
, 0);
680 SendMessage(hwndRichEdit
, EM_AUTOURLDETECT
, TRUE
, 0);
681 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) urls
[i
].text
);
682 SendMessage(hwndRichEdit
, WM_CHAR
, 0, 0);
683 check_CFE_LINK_rcvd(hwndRichEdit
, urls
[i
].is_url
);
685 DestroyWindow(hwndRichEdit
);
686 DestroyWindow(parent
);
689 static void test_EM_SCROLL(void)
692 int r
; /* return value */
693 int expr
; /* expected return value */
694 HWND hwndRichEdit
= new_richedit(NULL
);
695 int y_before
, y_after
; /* units of lines of text */
697 /* test a richedit box containing a single line of text */
698 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "a");/* one line of text */
700 for (i
= 0; i
< 4; i
++) {
701 static const int cmd
[4] = { SB_PAGEDOWN
, SB_PAGEUP
, SB_LINEDOWN
, SB_LINEUP
};
703 r
= SendMessage(hwndRichEdit
, EM_SCROLL
, cmd
[i
], 0);
704 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
705 ok(expr
== r
, "EM_SCROLL improper return value returned (i == %d). "
706 "Got 0x%08x, expected 0x%08x\n", i
, r
, expr
);
707 ok(y_after
== 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
708 "(i == %d)\n", y_after
, i
);
712 * test a richedit box that will scroll. There are two general
713 * cases: the case without any long lines and the case with a long
716 for (i
= 0; i
< 2; i
++) { /* iterate through different bodies of text */
718 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "a\nb\nc\nd\ne");
720 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
)
721 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
723 "LONG LINE \nb\nc\nd\ne");
724 for (j
= 0; j
< 12; j
++) /* reset scrol position to top */
725 SendMessage(hwndRichEdit
, EM_SCROLL
, SB_PAGEUP
, 0);
727 /* get first visible line */
728 y_before
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
729 r
= SendMessage(hwndRichEdit
, EM_SCROLL
, SB_PAGEDOWN
, 0); /* page down */
731 /* get new current first visible line */
732 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
734 ok(((r
& 0xffffff00) == 0x00010000) &&
735 ((r
& 0x000000ff) != 0x00000000),
736 "EM_SCROLL page down didn't scroll by a small positive number of "
737 "lines (r == 0x%08x)\n", r
);
738 ok(y_after
> y_before
, "EM_SCROLL page down not functioning "
739 "(line %d scrolled to line %d\n", y_before
, y_after
);
743 r
= SendMessage(hwndRichEdit
, EM_SCROLL
, SB_PAGEUP
, 0); /* page up */
744 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
745 ok(((r
& 0xffffff00) == 0x0001ff00),
746 "EM_SCROLL page up didn't scroll by a small negative number of lines "
747 "(r == 0x%08x)\n", r
);
748 ok(y_after
< y_before
, "EM_SCROLL page up not functioning (line "
749 "%d scrolled to line %d\n", y_before
, y_after
);
753 r
= SendMessage(hwndRichEdit
, EM_SCROLL
, SB_LINEDOWN
, 0); /* line down */
755 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
757 ok(r
== 0x00010001, "EM_SCROLL line down didn't scroll by one line "
758 "(r == 0x%08x)\n", r
);
759 ok(y_after
-1 == y_before
, "EM_SCROLL line down didn't go down by "
760 "1 line (%d scrolled to %d)\n", y_before
, y_after
);
764 r
= SendMessage(hwndRichEdit
, EM_SCROLL
, SB_LINEUP
, 0); /* line up */
766 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
768 ok(r
== 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
769 "(r == 0x%08x)\n", r
);
770 ok(y_after
+1 == y_before
, "EM_SCROLL line up didn't go up by 1 "
771 "line (%d scrolled to %d)\n", y_before
, y_after
);
775 r
= SendMessage(hwndRichEdit
, EM_SCROLL
,
776 SB_LINEUP
, 0); /* lineup beyond top */
778 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
781 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r
);
782 ok(y_before
== y_after
,
783 "EM_SCROLL line up beyond top worked (%d)\n", y_after
);
787 r
= SendMessage(hwndRichEdit
, EM_SCROLL
,
788 SB_PAGEUP
, 0);/*page up beyond top */
790 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
793 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r
);
794 ok(y_before
== y_after
,
795 "EM_SCROLL page up beyond top worked (%d)\n", y_after
);
797 for (j
= 0; j
< 12; j
++) /* page down all the way to the bottom */
798 SendMessage(hwndRichEdit
, EM_SCROLL
, SB_PAGEDOWN
, 0);
799 y_before
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
800 r
= SendMessage(hwndRichEdit
, EM_SCROLL
,
801 SB_PAGEDOWN
, 0); /* page down beyond bot */
802 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
805 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r
);
806 ok(y_before
== y_after
,
807 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
810 y_before
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
811 SendMessage(hwndRichEdit
, EM_SCROLL
,
812 SB_LINEDOWN
, 0); /* line down beyond bot */
813 y_after
= SendMessage(hwndRichEdit
, EM_GETFIRSTVISIBLELINE
, 0, 0);
816 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r
);
817 ok(y_before
== y_after
,
818 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
821 DestroyWindow(hwndRichEdit
);
824 static void test_EM_SETUNDOLIMIT(void)
826 /* cases we test for:
827 * default behaviour - limiting at 100 undo's
828 * undo disabled - setting a limit of 0
829 * undo limited - undo limit set to some to some number, like 2
830 * bad input - sending a negative number should default to 100 undo's */
832 HWND hwndRichEdit
= new_richedit(NULL
);
837 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "x");
840 SendMessage(hwndRichEdit
, WM_COPY
, 0, 0);
841 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
842 also, multiple pastes don't combine like WM_CHAR would */
843 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
845 /* first case - check the default */
846 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0,0);
847 for (i
=0; i
<101; i
++) /* Put 101 undo's on the stack */
848 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
849 for (i
=0; i
<100; i
++) /* Undo 100 of them */
850 SendMessage(hwndRichEdit
, WM_UNDO
, 0, 0);
851 ok(!SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0),
852 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
854 /* second case - cannot undo */
855 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0, 0);
856 SendMessage(hwndRichEdit
, EM_SETUNDOLIMIT
, 0, 0);
857 SendMessage(hwndRichEdit
,
858 WM_PASTE
, 0, 0); /* Try to put something in the undo stack */
859 ok(!SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0),
860 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
862 /* third case - set it to an arbitrary number */
863 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0, 0);
864 SendMessage(hwndRichEdit
, EM_SETUNDOLIMIT
, 2, 0);
865 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
866 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
867 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
868 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
869 ok(SendMessage(hwndRichEdit
, EM_CANUNDO
, 0,0),
870 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
871 SendMessage(hwndRichEdit
, WM_UNDO
, 0, 0);
872 ok(SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0),
873 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
874 SendMessage(hwndRichEdit
, WM_UNDO
, 0, 0);
875 ok(!SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0),
876 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
878 /* fourth case - setting negative numbers should default to 100 undos */
879 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0,0);
880 result
= SendMessage(hwndRichEdit
, EM_SETUNDOLIMIT
, -1, 0);
882 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result
);
884 DestroyWindow(hwndRichEdit
);
887 static void test_ES_PASSWORD(void)
889 /* This isn't hugely testable, so we're just going to run it through its paces */
891 HWND hwndRichEdit
= new_richedit(NULL
);
894 /* First, check the default of a regular control */
895 result
= SendMessage(hwndRichEdit
, EM_GETPASSWORDCHAR
, 0, 0);
897 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result
);
899 /* Now, set it to something normal */
900 SendMessage(hwndRichEdit
, EM_SETPASSWORDCHAR
, 'x', 0);
901 result
= SendMessage(hwndRichEdit
, EM_GETPASSWORDCHAR
, 0, 0);
903 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result
,result
);
905 /* Now, set it to something odd */
906 SendMessage(hwndRichEdit
, EM_SETPASSWORDCHAR
, (WCHAR
)1234, 0);
907 result
= SendMessage(hwndRichEdit
, EM_GETPASSWORDCHAR
, 0, 0);
909 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result
,result
);
910 DestroyWindow(hwndRichEdit
);
913 static void test_EM_SETTEXTEX(void)
915 HWND hwndRichEdit
= new_richedit(NULL
);
918 WCHAR TestItem1
[] = {'T', 'e', 's', 't',
920 'T', 'e', 'x', 't', 0};
921 #define MAX_BUF_LEN 1024
922 WCHAR buf
[MAX_BUF_LEN
];
926 setText
.codepage
= 1200; /* no constant for unicode */
927 getText
.codepage
= 1200; /* no constant for unicode */
928 getText
.cb
= MAX_BUF_LEN
;
929 getText
.flags
= GT_DEFAULT
;
932 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
) TestItem1
);
933 SendMessage(hwndRichEdit
, EM_GETTEXTEX
, (WPARAM
)&getText
, (LPARAM
) buf
);
934 ok(lstrcmpW(buf
, TestItem1
) == 0,
935 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
937 result
= SendMessage(hwndRichEdit
, EM_SETTEXTEX
,
938 (WPARAM
)&setText
, (LPARAM
) NULL
);
939 SendMessage(hwndRichEdit
, EM_GETTEXTEX
, (WPARAM
)&getText
, (LPARAM
) buf
);
942 "EM_SETTEXTEX returned %d, instead of 1\n",result
);
943 ok(lstrlenW(buf
) == 0,
944 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
946 /* put some text back */
948 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
) TestItem1
);
949 /* select some text */
952 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
953 /* replace current selection */
954 setText
.flags
= ST_SELECTION
;
955 result
= SendMessage(hwndRichEdit
, EM_SETTEXTEX
,
956 (WPARAM
)&setText
, (LPARAM
) NULL
);
958 "EM_SETTEXTEX with NULL lParam to replace selection"
959 " with no text should return 0. Got %i\n",
962 /* put some text back */
964 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
) TestItem1
);
965 /* select some text */
968 SendMessage(hwndRichEdit
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
969 /* replace current selection */
970 setText
.flags
= ST_SELECTION
;
971 result
= SendMessage(hwndRichEdit
, EM_SETTEXTEX
,
972 (WPARAM
)&setText
, (LPARAM
) TestItem1
);
974 SendMessage(hwndRichEdit
, EM_GETTEXTEX
, (WPARAM
)&getText
, (LPARAM
) buf
);
975 ok(result
== lstrlenW(TestItem1
),
976 "EM_SETTEXTEX with NULL lParam to replace selection"
977 " with no text should return 0. Got %i\n",
979 ok(lstrlenW(buf
) == 22,
980 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
983 DestroyWindow(hwndRichEdit
);
986 static void test_EM_LIMITTEXT(void)
990 HWND hwndRichEdit
= new_richedit(NULL
);
992 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
993 * about setting the length to -1 for multiline edit controls doesn't happen.
996 /* Don't check default gettextlimit case. That's done in other tests */
998 /* Set textlimit to 100 */
999 SendMessage (hwndRichEdit
, EM_LIMITTEXT
, 100, 0);
1000 ret
= SendMessage (hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1002 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret
);
1004 /* Set textlimit to 0 */
1005 SendMessage (hwndRichEdit
, EM_LIMITTEXT
, 0, 0);
1006 ret
= SendMessage (hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1008 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret
);
1010 /* Set textlimit to -1 */
1011 SendMessage (hwndRichEdit
, EM_LIMITTEXT
, -1, 0);
1012 ret
= SendMessage (hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1014 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret
);
1016 /* Set textlimit to -2 */
1017 SendMessage (hwndRichEdit
, EM_LIMITTEXT
, -2, 0);
1018 ret
= SendMessage (hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1020 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret
);
1022 DestroyWindow (hwndRichEdit
);
1026 static void test_EM_EXLIMITTEXT(void)
1028 int i
, selBegin
, selEnd
, len1
, len2
;
1030 char text
[1024 + 1];
1031 char buffer
[1024 + 1];
1032 int textlimit
= 0; /* multiple of 100 */
1033 HWND hwndRichEdit
= new_richedit(NULL
);
1035 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1036 ok(32767 == i
, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i
); /* default */
1039 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, textlimit
);
1040 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1042 ok(textlimit
== i
, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit
, i
);
1045 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, textlimit
);
1046 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1048 ok(textlimit
== i
, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit
, i
);
1050 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, 0);
1051 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1052 /* default for WParam = 0 */
1053 ok(65536 == i
, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i
);
1055 textlimit
= sizeof(text
)-1;
1056 memset(text
, 'W', textlimit
);
1057 text
[sizeof(text
)-1] = 0;
1058 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, textlimit
);
1059 /* maxed out text */
1060 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
1062 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, -1); /* select everything */
1063 SendMessage(hwndRichEdit
, EM_GETSEL
, (WPARAM
)&selBegin
, (LPARAM
)&selEnd
);
1064 len1
= selEnd
- selBegin
;
1066 SendMessage(hwndRichEdit
, WM_KEYDOWN
, VK_BACK
, 1);
1067 SendMessage(hwndRichEdit
, WM_CHAR
, VK_BACK
, 1);
1068 SendMessage(hwndRichEdit
, WM_KEYUP
, VK_BACK
, 1);
1069 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, -1);
1070 SendMessage(hwndRichEdit
, EM_GETSEL
, (WPARAM
)&selBegin
, (LPARAM
)&selEnd
);
1071 len2
= selEnd
- selBegin
;
1074 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1077 SendMessage(hwndRichEdit
, WM_KEYDOWN
, 'A', 1);
1078 SendMessage(hwndRichEdit
, WM_CHAR
, 'A', 1);
1079 SendMessage(hwndRichEdit
, WM_KEYUP
, 'A', 1);
1080 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, -1);
1081 SendMessage(hwndRichEdit
, EM_GETSEL
, (WPARAM
)&selBegin
, (LPARAM
)&selEnd
);
1082 len1
= selEnd
- selBegin
;
1085 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1088 SendMessage(hwndRichEdit
, WM_KEYDOWN
, 'A', 1);
1089 SendMessage(hwndRichEdit
, WM_CHAR
, 'A', 1);
1090 SendMessage(hwndRichEdit
, WM_KEYUP
, 'A', 1); /* full; should be no effect */
1091 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, -1);
1092 SendMessage(hwndRichEdit
, EM_GETSEL
, (WPARAM
)&selBegin
, (LPARAM
)&selEnd
);
1093 len2
= selEnd
- selBegin
;
1096 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1099 /* set text up to the limit, select all the text, then add a char */
1101 memset(text
, 'W', textlimit
);
1102 text
[textlimit
] = 0;
1103 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, textlimit
);
1104 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text
);
1105 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, -1);
1106 SendMessage(hwndRichEdit
, WM_CHAR
, 'A', 1);
1107 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1108 result
= strcmp(buffer
, "A");
1109 ok(0 == result
, "got string = \"%s\"\n", buffer
);
1111 DestroyWindow(hwndRichEdit
);
1114 static void test_EM_GETLIMITTEXT(void)
1117 HWND hwndRichEdit
= new_richedit(NULL
);
1119 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1120 ok(32767 == i
, "expected: %d, actual: %d\n", 32767, i
); /* default value */
1122 SendMessage(hwndRichEdit
, EM_EXLIMITTEXT
, 0, 50000);
1123 i
= SendMessage(hwndRichEdit
, EM_GETLIMITTEXT
, 0, 0);
1124 ok(50000 == i
, "expected: %d, actual: %d\n", 50000, i
);
1126 DestroyWindow(hwndRichEdit
);
1129 static void test_WM_SETFONT(void)
1131 /* There is no invalid input or error conditions for this function.
1132 * NULL wParam and lParam just fall back to their default values
1133 * It should be noted that even if you use a gibberish name for your fonts
1134 * here, it will still work because the name is stored. They will display as
1135 * System, but will report their name to be whatever they were created as */
1137 HWND hwndRichEdit
= new_richedit(NULL
);
1138 HFONT testFont1
= CreateFontA (0,0,0,0,FW_LIGHT
, 0, 0, 0, ANSI_CHARSET
,
1139 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
, DEFAULT_PITCH
|
1140 FF_DONTCARE
, "Marlett");
1141 HFONT testFont2
= CreateFontA (0,0,0,0,FW_LIGHT
, 0, 0, 0, ANSI_CHARSET
,
1142 OUT_TT_PRECIS
, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
, DEFAULT_PITCH
|
1143 FF_DONTCARE
, "MS Sans Serif");
1144 HFONT testFont3
= CreateFontA (0,0,0,0,FW_LIGHT
, 0, 0, 0, ANSI_CHARSET
,
1145 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
, DEFAULT_PITCH
|
1146 FF_DONTCARE
, "Courier");
1147 LOGFONTA sentLogFont
;
1148 CHARFORMAT2A returnedCF2A
;
1150 returnedCF2A
.cbSize
= sizeof(returnedCF2A
);
1152 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "x");
1153 SendMessage(hwndRichEdit
, WM_SETFONT
, (WPARAM
)testFont1
,(LPARAM
) MAKELONG((WORD
) TRUE
, 0));
1154 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, SCF_DEFAULT
, (LPARAM
) &returnedCF2A
);
1156 GetObjectA(testFont1
, sizeof(LOGFONTA
), &sentLogFont
);
1157 ok (!strcmp(sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
),
1158 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1159 sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
);
1161 SendMessage(hwndRichEdit
, WM_SETFONT
, (WPARAM
)testFont2
,(LPARAM
) MAKELONG((WORD
) TRUE
, 0));
1162 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, SCF_DEFAULT
, (LPARAM
) &returnedCF2A
);
1163 GetObjectA(testFont2
, sizeof(LOGFONTA
), &sentLogFont
);
1164 ok (!strcmp(sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
),
1165 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1166 sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
);
1168 SendMessage(hwndRichEdit
, WM_SETFONT
, (WPARAM
)testFont3
,(LPARAM
) MAKELONG((WORD
) TRUE
, 0));
1169 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, SCF_DEFAULT
, (LPARAM
) &returnedCF2A
);
1170 GetObjectA(testFont3
, sizeof(LOGFONTA
), &sentLogFont
);
1171 ok (!strcmp(sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
),
1172 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1173 sentLogFont
.lfFaceName
,returnedCF2A
.szFaceName
);
1175 /* This last test is special since we send in NULL. We clear the variables
1176 * and just compare to "System" instead of the sent in font name. */
1177 ZeroMemory(&returnedCF2A
,sizeof(returnedCF2A
));
1178 ZeroMemory(&sentLogFont
,sizeof(sentLogFont
));
1179 returnedCF2A
.cbSize
= sizeof(returnedCF2A
);
1181 SendMessage(hwndRichEdit
, WM_SETFONT
, (WPARAM
)NULL
,(LPARAM
) MAKELONG((WORD
) TRUE
, 0));
1182 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, SCF_DEFAULT
, (LPARAM
) &returnedCF2A
);
1183 GetObjectA(NULL
, sizeof(LOGFONTA
), &sentLogFont
);
1184 ok (!strcmp("System",returnedCF2A
.szFaceName
),
1185 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A
.szFaceName
);
1187 DestroyWindow(hwndRichEdit
);
1191 static DWORD CALLBACK
test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie
,
1196 const char** str
= (const char**)dwCookie
;
1197 int size
= strlen(*str
);
1198 if(size
> 3) /* let's make it peice-meal for fun */
1205 memcpy(pbBuff
, *str
, *pcb
);
1211 static void test_EM_GETMODIFY(void)
1213 HWND hwndRichEdit
= new_richedit(NULL
);
1216 WCHAR TestItem1
[] = {'T', 'e', 's', 't',
1218 'T', 'e', 'x', 't', 0};
1219 WCHAR TestItem2
[] = {'T', 'e', 's', 't',
1221 'O', 't', 'h', 'e', 'r',
1222 'T', 'e', 'x', 't', 0};
1223 const char* streamText
= "hello world";
1228 HFONT testFont
= CreateFontA (0,0,0,0,FW_LIGHT
, 0, 0, 0, ANSI_CHARSET
,
1229 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
, DEFAULT_PITCH
|
1230 FF_DONTCARE
, "Courier");
1232 setText
.codepage
= 1200; /* no constant for unicode */
1233 setText
.flags
= ST_KEEPUNDO
;
1236 /* modify flag shouldn't be set when richedit is first created */
1237 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1239 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1241 /* setting modify flag should actually set it */
1242 SendMessage(hwndRichEdit
, EM_SETMODIFY
, TRUE
, 0);
1243 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1245 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1247 /* clearing modify flag should actually clear it */
1248 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1249 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1251 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1253 /* setting font doesn't change modify flag */
1254 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1255 SendMessage(hwndRichEdit
, WM_SETFONT
, (WPARAM
)testFont
,(LPARAM
) MAKELONG((WORD
) TRUE
, 0));
1256 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1258 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1260 /* setting text should set modify flag */
1261 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1262 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
)TestItem1
);
1263 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1265 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1267 /* undo previous text doesn't reset modify flag */
1268 SendMessage(hwndRichEdit
, WM_UNDO
, 0, 0);
1269 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1271 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1273 /* set text with no flag to keep undo stack should not set modify flag */
1274 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1276 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
)TestItem1
);
1277 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1279 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1281 /* WM_SETTEXT doesn't modify */
1282 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1283 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
)TestItem2
);
1284 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1287 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1290 /* clear the text */
1291 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1292 SendMessage(hwndRichEdit
, WM_CLEAR
, 0, 0);
1293 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1295 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1298 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1299 SendMessage(hwndRichEdit
, EM_SETTEXTEX
, (WPARAM
)&setText
, (LPARAM
)TestItem1
);
1300 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 2);
1301 SendMessage(hwndRichEdit
, EM_REPLACESEL
, TRUE
, (LPARAM
)TestItem2
);
1302 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1304 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1306 /* copy/paste text 1 */
1307 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1308 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 2);
1309 SendMessage(hwndRichEdit
, WM_COPY
, 0, 0);
1310 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
1311 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1313 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1315 /* copy/paste text 2 */
1316 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1317 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 2);
1318 SendMessage(hwndRichEdit
, WM_COPY
, 0, 0);
1319 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 3);
1320 SendMessage(hwndRichEdit
, WM_PASTE
, 0, 0);
1321 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1323 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1326 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1327 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 1);
1328 SendMessage(hwndRichEdit
, WM_CHAR
, 'A', 0);
1329 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1331 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1334 SendMessage(hwndRichEdit
, WM_CHAR
, 'A', 0);
1335 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1336 SendMessage(hwndRichEdit
, WM_KEYDOWN
, VK_BACK
, 0);
1337 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1339 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1341 /* set char format */
1342 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1343 cf2
.cbSize
= sizeof(CHARFORMAT2
);
1344 SendMessage(hwndRichEdit
, EM_GETCHARFORMAT
, (WPARAM
) SCF_DEFAULT
,
1346 cf2
.dwMask
= CFM_ITALIC
| cf2
.dwMask
;
1347 cf2
.dwEffects
= CFE_ITALIC
^ cf2
.dwEffects
;
1348 SendMessage(hwndRichEdit
, EM_SETCHARFORMAT
, (WPARAM
) SCF_ALL
, (LPARAM
) &cf2
);
1349 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1351 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1353 /* set para format */
1354 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1355 pf2
.cbSize
= sizeof(PARAFORMAT2
);
1356 SendMessage(hwndRichEdit
, EM_GETPARAFORMAT
, 0,
1358 pf2
.dwMask
= PFM_ALIGNMENT
| pf2
.dwMask
;
1359 pf2
.wAlignment
= PFA_RIGHT
;
1360 SendMessage(hwndRichEdit
, EM_SETPARAFORMAT
, 0, (LPARAM
) &pf2
);
1361 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1363 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1366 SendMessage(hwndRichEdit
, EM_SETMODIFY
, FALSE
, 0);
1367 es
.dwCookie
= (DWORD_PTR
)&streamText
;
1369 es
.pfnCallback
= test_EM_GETMODIFY_esCallback
;
1370 SendMessage(hwndRichEdit
, EM_STREAMIN
,
1371 (WPARAM
)(SF_TEXT
), (LPARAM
)&es
);
1372 result
= SendMessage(hwndRichEdit
, EM_GETMODIFY
, 0, 0);
1374 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1376 DestroyWindow(hwndRichEdit
);
1382 long expected_retval
;
1383 int expected_getsel_start
;
1384 int expected_getsel_end
;
1385 int _exsetsel_todo_wine
;
1386 int _getsel_todo_wine
;
1389 const struct exsetsel_s exsetsel_tests
[] = {
1391 {5, 10, 10, 5, 10, 0, 0},
1392 {15, 17, 17, 15, 17, 0, 0},
1393 /* test cpMax > strlen() */
1394 {0, 100, 18, 0, 18, 0, 1},
1395 /* test cpMin == cpMax */
1396 {5, 5, 5, 5, 5, 0, 0},
1397 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1398 {-1, 0, 5, 5, 5, 0, 0},
1399 {-1, 17, 5, 5, 5, 0, 0},
1400 {-1, 18, 5, 5, 5, 0, 0},
1401 /* test cpMin < 0 && cpMax < 0 */
1402 {-1, -1, 17, 17, 17, 0, 0},
1403 {-4, -5, 17, 17, 17, 0, 0},
1404 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1405 {0, -1, 18, 0, 18, 0, 1},
1406 {17, -5, 18, 17, 18, 0, 1},
1407 {18, -3, 17, 17, 17, 0, 0},
1408 /* test if cpMin > cpMax */
1409 {15, 19, 18, 15, 18, 0, 1},
1410 {19, 15, 18, 15, 18, 0, 1}
1413 static void check_EM_EXSETSEL(HWND hwnd
, const struct exsetsel_s
*setsel
, int id
) {
1418 cr
.cpMin
= setsel
->min
;
1419 cr
.cpMax
= setsel
->max
;
1420 result
= SendMessage(hwnd
, EM_EXSETSEL
, 0, (LPARAM
) &cr
);
1422 if (setsel
->_exsetsel_todo_wine
) {
1424 ok(result
== setsel
->expected_retval
, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id
, setsel
->expected_retval
, result
);
1427 ok(result
== setsel
->expected_retval
, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id
, setsel
->expected_retval
, result
);
1430 SendMessage(hwnd
, EM_GETSEL
, (WPARAM
) &start
, (LPARAM
) &end
);
1432 if (setsel
->_getsel_todo_wine
) {
1434 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
);
1437 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
);
1441 static void test_EM_EXSETSEL(void)
1443 HWND hwndRichEdit
= new_richedit(NULL
);
1445 const int num_tests
= sizeof(exsetsel_tests
)/sizeof(struct exsetsel_s
);
1447 /* sending some text to the window */
1448 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "testing selection");
1449 /* 01234567890123456*/
1452 for (i
= 0; i
< num_tests
; i
++) {
1453 check_EM_EXSETSEL(hwndRichEdit
, &exsetsel_tests
[i
], i
);
1456 DestroyWindow(hwndRichEdit
);
1459 static void test_EM_REPLACESEL(void)
1461 HWND hwndRichEdit
= new_richedit(NULL
);
1462 char buffer
[1024] = {0};
1465 /* sending some text to the window */
1466 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) "testing selection");
1467 /* 01234567890123456*/
1470 /* FIXME add more tests */
1471 SendMessage(hwndRichEdit
, EM_SETSEL
, 7, 17);
1472 SendMessage(hwndRichEdit
, EM_REPLACESEL
, 0, (LPARAM
) NULL
);
1473 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1474 r
= strcmp(buffer
, "testing");
1475 ok(0 == r
, "expected %d, got %d\n", 0, r
);
1477 DestroyWindow(hwndRichEdit
);
1480 static void test_WM_PASTE(void)
1483 char buffer
[1024] = {0};
1484 const char* text1
= "testing paste\r";
1485 const char* text2
= "testing paste\r\rtesting paste";
1486 const char* text3
= "testing paste\rpaste\rtesting paste";
1487 HWND hwndRichEdit
= new_richedit(NULL
);
1489 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text1
);
1490 SendMessage(hwndRichEdit
, EM_SETSEL
, 0, 14);
1491 SendMessage(hwndRichEdit
, WM_CHAR
, 3, 0); /* ctrl-c */
1492 SendMessage(hwndRichEdit
, EM_SETSEL
, 14, 14);
1493 SendMessage(hwndRichEdit
, WM_CHAR
, 22, 0); /* ctrl-v */
1494 SendMessage(hwndRichEdit
, WM_CHAR
, 26, 0); /* ctrl-z */
1495 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1496 result
= strcmp(text1
, buffer
);
1498 "test paste: strcmp = %i\n", result
);
1500 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) text2
);
1501 SendMessage(hwndRichEdit
, EM_SETSEL
, 8, 13);
1502 SendMessage(hwndRichEdit
, WM_CHAR
, 3, 0); /* ctrl-c */
1503 SendMessage(hwndRichEdit
, EM_SETSEL
, 14, 14);
1504 SendMessage(hwndRichEdit
, WM_CHAR
, 22, 0); /* ctrl-v */
1505 SendMessage(hwndRichEdit
, WM_CHAR
, 26, 0); /* ctrl-z */
1506 SendMessage(hwndRichEdit
, WM_CHAR
, 25, 0); /* ctrl-y */
1507 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1508 result
= strcmp(buffer
,text3
);
1510 "test paste: strcmp = %i\n", result
);
1512 DestroyWindow(hwndRichEdit
);
1515 static void test_EM_FORMATRANGE(void)
1520 HWND hwndRichEdit
= new_richedit(NULL
);
1522 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) haystack
);
1524 hdc
= GetDC(hwndRichEdit
);
1525 ok(hdc
!= NULL
, "Could not get HDC\n");
1527 fr
.hdc
= fr
.hdcTarget
= hdc
;
1528 fr
.rc
.top
= fr
.rcPage
.top
= fr
.rc
.left
= fr
.rcPage
.left
= 0;
1529 fr
.rc
.right
= fr
.rcPage
.right
= GetDeviceCaps(hdc
, HORZRES
);
1530 fr
.rc
.bottom
= fr
.rcPage
.bottom
= GetDeviceCaps(hdc
, VERTRES
);
1534 r
= SendMessage(hwndRichEdit
, EM_FORMATRANGE
, TRUE
, (LPARAM
) NULL
);
1536 ok(r
== 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r
);
1539 r
= SendMessage(hwndRichEdit
, EM_FORMATRANGE
, TRUE
, (LPARAM
) &fr
);
1541 ok(r
== 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r
);
1547 r
= SendMessage(hwndRichEdit
, EM_FORMATRANGE
, TRUE
, (LPARAM
) &fr
);
1549 ok(r
== 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r
);
1552 r
= SendMessage(hwndRichEdit
, EM_FORMATRANGE
, TRUE
, (LPARAM
) NULL
);
1554 ok(r
== 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r
);
1557 DestroyWindow(hwndRichEdit
);
1560 static int nCallbackCount
= 0;
1562 static DWORD CALLBACK
EditStreamCallback(DWORD_PTR dwCookie
, LPBYTE pbBuff
,
1565 const char text
[] = {'t','e','s','t'};
1567 if (sizeof(text
) <= cb
)
1569 if ((int)dwCookie
!= nCallbackCount
)
1575 memcpy (pbBuff
, text
, sizeof(text
));
1576 *pcb
= sizeof(text
);
1583 return 1; /* indicates callback failed */
1586 static void test_EM_StreamIn_Undo(void)
1588 /* The purpose of this test is to determine when a EM_StreamIn should be
1589 * undoable. This is important because WM_PASTE currently uses StreamIn and
1590 * pasting should always be undoable but streaming isn't always.
1593 * StreamIn plain text without SFF_SELECTION.
1594 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1595 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1596 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1597 * Feel free to add tests for other text modes or StreamIn things.
1601 HWND hwndRichEdit
= new_richedit(NULL
);
1604 char buffer
[1024] = {0};
1605 const char randomtext
[] = "Some text";
1607 es
.pfnCallback
= (EDITSTREAMCALLBACK
) EditStreamCallback
;
1609 /* StreamIn, no SFF_SELECTION */
1610 es
.dwCookie
= nCallbackCount
;
1611 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0,0);
1612 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) randomtext
);
1613 SendMessage(hwndRichEdit
, EM_SETSEL
,0,0);
1614 SendMessage(hwndRichEdit
, EM_STREAMIN
, (WPARAM
)SF_TEXT
, (LPARAM
)&es
);
1615 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1616 result
= strcmp (buffer
,"test");
1618 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer
);
1620 result
= SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0);
1621 ok (result
== FALSE
,
1622 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1624 /* StreamIn, SFF_SELECTION, but nothing selected */
1625 es
.dwCookie
= nCallbackCount
;
1626 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0,0);
1627 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) randomtext
);
1628 SendMessage(hwndRichEdit
, EM_SETSEL
,0,0);
1629 SendMessage(hwndRichEdit
, EM_STREAMIN
,
1630 (WPARAM
)(SF_TEXT
|SFF_SELECTION
), (LPARAM
)&es
);
1631 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1632 result
= strcmp (buffer
,"testSome text");
1634 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer
);
1636 result
= SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0);
1638 "EM_STREAMIN with SFF_SELECTION but no selection set "
1639 "should create an undo\n");
1641 /* StreamIn, SFF_SELECTION, with a selection */
1642 es
.dwCookie
= nCallbackCount
;
1643 SendMessage(hwndRichEdit
,EM_EMPTYUNDOBUFFER
, 0,0);
1644 SendMessage(hwndRichEdit
, WM_SETTEXT
, 0, (LPARAM
) randomtext
);
1645 SendMessage(hwndRichEdit
, EM_SETSEL
,4,5);
1646 SendMessage(hwndRichEdit
, EM_STREAMIN
,
1647 (WPARAM
)(SF_TEXT
|SFF_SELECTION
), (LPARAM
)&es
);
1648 SendMessage(hwndRichEdit
, WM_GETTEXT
, 1024, (LPARAM
) buffer
);
1649 result
= strcmp (buffer
,"Sometesttext");
1651 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer
);
1653 result
= SendMessage(hwndRichEdit
, EM_CANUNDO
, 0, 0);
1655 "EM_STREAMIN with SFF_SELECTION and selection set "
1656 "should create an undo\n");
1660 static BOOL
is_em_settextex_supported(HWND hwnd
)
1662 SETTEXTEX stex
= { ST_DEFAULT
, CP_ACP
};
1663 return SendMessageA(hwnd
, EM_SETTEXTEX
, (WPARAM
)&stex
, 0) != 0;
1666 static void test_unicode_conversions(void)
1668 static const WCHAR tW
[] = {'t',0};
1669 static const WCHAR teW
[] = {'t','e',0};
1670 static const WCHAR textW
[] = {'t','e','s','t',0};
1671 static const char textA
[] = "test";
1675 int is_win9x
, em_settextex_supported
, ret
;
1677 is_win9x
= GetVersion() & 0x80000000;
1679 #define set_textA(hwnd, wm_set_text, txt) \
1681 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1682 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1683 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1684 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1685 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1687 #define expect_textA(hwnd, wm_get_text, txt) \
1689 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1690 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1691 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1692 memset(bufA, 0xAA, sizeof(bufA)); \
1693 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1694 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1695 ret = lstrcmpA(bufA, txt); \
1696 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1699 #define set_textW(hwnd, wm_set_text, txt) \
1701 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1702 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1703 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1704 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1705 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1707 #define expect_textW(hwnd, wm_get_text, txt) \
1709 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1710 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1711 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1712 memset(bufW, 0xAA, sizeof(bufW)); \
1715 assert(wm_get_text == EM_GETTEXTEX); \
1716 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1717 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1721 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1722 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1724 ret = lstrcmpW(bufW, txt); \
1725 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1727 #define expect_empty(hwnd, wm_get_text) \
1729 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1730 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1731 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1732 memset(bufA, 0xAA, sizeof(bufA)); \
1733 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1734 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1735 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1738 hwnd
= CreateWindowExA(0, "RichEdit20W", NULL
, WS_POPUP
,
1739 0, 0, 200, 60, 0, 0, 0, 0);
1740 ok(hwnd
!= 0, "CreateWindowExA error %u\n", GetLastError());
1742 ret
= IsWindowUnicode(hwnd
);
1744 ok(!ret
, "RichEdit20W should NOT be unicode under Win9x\n");
1746 ok(ret
, "RichEdit20W should be unicode under NT\n");
1748 /* EM_SETTEXTEX is supported starting from version 3.0 */
1749 em_settextex_supported
= is_em_settextex_supported(hwnd
);
1750 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1751 em_settextex_supported
? "" : "NOT ");
1753 expect_empty(hwnd
, WM_GETTEXT
);
1754 expect_empty(hwnd
, EM_GETTEXTEX
);
1756 ret
= SendMessageA(hwnd
, WM_CHAR
, (WPARAM
)textW
[0], 0);
1757 ok(!ret
, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret
);
1758 expect_textA(hwnd
, WM_GETTEXT
, "t");
1759 expect_textA(hwnd
, EM_GETTEXTEX
, "t");
1760 expect_textW(hwnd
, EM_GETTEXTEX
, tW
);
1762 ret
= SendMessageA(hwnd
, WM_CHAR
, (WPARAM
)textA
[1], 0);
1763 ok(!ret
, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret
);
1764 expect_textA(hwnd
, WM_GETTEXT
, "te");
1765 expect_textA(hwnd
, EM_GETTEXTEX
, "te");
1766 expect_textW(hwnd
, EM_GETTEXTEX
, teW
);
1768 set_textA(hwnd
, WM_SETTEXT
, NULL
);
1769 expect_empty(hwnd
, WM_GETTEXT
);
1770 expect_empty(hwnd
, EM_GETTEXTEX
);
1773 set_textA(hwnd
, WM_SETTEXT
, textW
);
1775 set_textA(hwnd
, WM_SETTEXT
, textA
);
1776 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1777 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1778 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1780 if (em_settextex_supported
)
1782 set_textA(hwnd
, EM_SETTEXTEX
, textA
);
1783 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1784 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1785 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1790 set_textW(hwnd
, WM_SETTEXT
, textW
);
1791 expect_textW(hwnd
, WM_GETTEXT
, textW
);
1792 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1793 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1794 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1796 if (em_settextex_supported
)
1798 set_textW(hwnd
, EM_SETTEXTEX
, textW
);
1799 expect_textW(hwnd
, WM_GETTEXT
, textW
);
1800 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1801 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1802 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1805 DestroyWindow(hwnd
);
1807 hwnd
= CreateWindowExA(0, "RichEdit20A", NULL
, WS_POPUP
,
1808 0, 0, 200, 60, 0, 0, 0, 0);
1809 ok(hwnd
!= 0, "CreateWindowExA error %u\n", GetLastError());
1811 ret
= IsWindowUnicode(hwnd
);
1812 ok(!ret
, "RichEdit20A should NOT be unicode\n");
1814 set_textA(hwnd
, WM_SETTEXT
, textA
);
1815 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1816 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1817 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1819 if (em_settextex_supported
)
1821 set_textA(hwnd
, EM_SETTEXTEX
, textA
);
1822 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1823 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1824 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1829 set_textW(hwnd
, WM_SETTEXT
, textW
);
1830 expect_textW(hwnd
, WM_GETTEXT
, textW
);
1831 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1832 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1833 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1835 if (em_settextex_supported
)
1837 set_textW(hwnd
, EM_SETTEXTEX
, textW
);
1838 expect_textW(hwnd
, WM_GETTEXT
, textW
);
1839 expect_textA(hwnd
, WM_GETTEXT
, textA
);
1840 expect_textW(hwnd
, EM_GETTEXTEX
, textW
);
1841 expect_textA(hwnd
, EM_GETTEXTEX
, textA
);
1844 DestroyWindow(hwnd
);
1848 static void test_EM_GETTEXTLENGTHEX(void)
1851 GETTEXTLENGTHEX gtl
;
1855 hwnd
= CreateWindowExA(0, "RichEdit20W", NULL
, WS_POPUP
,
1856 0, 0, 200, 60, 0, 0, 0, 0);
1857 ok(hwnd
!= 0, "CreateWindowExA error %u\n", GetLastError());
1859 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
| GTL_USECRLF
;
1860 gtl
.codepage
= CP_ACP
;
1861 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1862 ok(ret
== 0, "ret %d\n",ret
);
1864 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
;
1865 gtl
.codepage
= CP_ACP
;
1866 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1867 ok(ret
== 0, "ret %d\n",ret
);
1869 SendMessage(hwnd
, WM_SETTEXT
, 0, (LPARAM
) "a\nb\n\n\r\n");
1871 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
| GTL_USECRLF
;
1872 gtl
.codepage
= CP_ACP
;
1873 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1874 todo_wine
ok(ret
== 1, "ret %d\n",ret
);
1876 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
;
1877 gtl
.codepage
= CP_ACP
;
1878 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1879 todo_wine
ok(ret
== 1, "ret %d\n",ret
);
1881 DestroyWindow(hwnd
);
1884 hwnd
= CreateWindowExA(0, "RichEdit20W", NULL
, WS_POPUP
| ES_MULTILINE
,
1885 0, 0, 200, 60, 0, 0, 0, 0);
1886 ok(hwnd
!= 0, "CreateWindowExA error %u\n", GetLastError());
1888 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
| GTL_USECRLF
;
1889 gtl
.codepage
= CP_ACP
;
1890 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1891 todo_wine
ok(ret
== 0, "ret %d\n",ret
);
1893 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
;
1894 gtl
.codepage
= CP_ACP
;
1895 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1896 ok(ret
== 0, "ret %d\n",ret
);
1898 SendMessage(hwnd
, WM_SETTEXT
, 0, (LPARAM
) "a\nb\n\n\r\n");
1900 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
| GTL_USECRLF
;
1901 gtl
.codepage
= CP_ACP
;
1902 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1903 todo_wine
ok(ret
== 10, "ret %d\n",ret
);
1905 gtl
.flags
= GTL_NUMCHARS
| GTL_PRECISE
;
1906 gtl
.codepage
= CP_ACP
;
1907 ret
= SendMessageA(hwnd
, EM_GETTEXTLENGTHEX
, (WPARAM
)>l
, 0);
1908 ok(ret
== 6, "ret %d\n",ret
);
1910 DestroyWindow(hwnd
);
1913 START_TEST( editor
)
1918 /* Must explicitly LoadLibrary(). The test has no references to functions in
1919 * RICHED20.DLL, so the linker doesn't actually link to it. */
1920 hmoduleRichEdit
= LoadLibrary("RICHED20.DLL");
1921 ok(hmoduleRichEdit
!= NULL
, "error: %d\n", (int) GetLastError());
1925 test_EM_SCROLLCARET();
1927 test_EM_SETTEXTMODE();
1928 test_TM_PLAINTEXT();
1929 test_EM_SETOPTIONS();
1931 test_EM_AUTOURLDETECT();
1932 test_EM_SETUNDOLIMIT();
1934 test_EM_SETTEXTEX();
1935 test_EM_LIMITTEXT();
1936 test_EM_EXLIMITTEXT();
1937 test_EM_GETLIMITTEXT();
1939 test_EM_GETMODIFY();
1942 test_EM_StreamIn_Undo();
1943 test_EM_FORMATRANGE();
1944 test_unicode_conversions();
1945 test_EM_GETTEXTLENGTHEX();
1946 test_EM_REPLACESEL();
1948 /* Set the environment variable WINETEST_RICHED20 to keep windows
1949 * responsive and open for 30 seconds. This is useful for debugging.
1951 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1952 * 50ms before retrying the queue. */
1953 end
= time(NULL
) + 30;
1954 if (getenv( "WINETEST_RICHED20" )) {
1955 while (time(NULL
) < end
) {
1956 if (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
1957 TranslateMessage(&msg
);
1958 DispatchMessage(&msg
);
1965 OleFlushClipboard();
1966 ok(FreeLibrary(hmoduleRichEdit
) != 0, "error: %d\n", (int) GetLastError());