riched20/tests: Add '\n's to ok() calls.
[wine] / dlls / riched20 / tests / 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)
702 {
703   CHARFORMAT2W text_format;
704   int link_present = 0;
705   text_format.cbSize = sizeof(text_format);
706   SendMessage(hwnd, EM_SETSEL, 0, 0);
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.\n");
712   }
713   else 
714   {
715     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
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);
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);
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   char buf[1024] = {0};
1007   LRESULT result;
1008
1009   /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1010      any solitary \r to be converted to \r\n on return. Properly paired
1011      \r\n are not affected.
1012    */
1013
1014   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1015   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1016   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1017   ok (result == strlen(buf),
1018         "WM_GETTEXT returned %ld instead of expected %u\n",
1019         result, strlen(buf));
1020   result = strcmp(TestItem1, buf);
1021   ok(result == 0,
1022         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1023
1024   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
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(TestItem2_after, buf);
1031   ok(result == 0,
1032         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1033
1034   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1035   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1036   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1037   ok (result == strlen(buf),
1038         "WM_GETTEXT returned %ld instead of expected %u\n",
1039         result, strlen(buf));
1040   result = strcmp(TestItem3_after, buf);
1041   ok(result == 0,
1042         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1043
1044   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3_after);
1045   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1046   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1047   ok (result == strlen(buf),
1048         "WM_GETTEXT returned %ld instead of expected %u\n",
1049         result, strlen(buf));
1050   result = strcmp(TestItem3_after, buf);
1051   ok(result == 0,
1052         "WM_SETTEXT round trip: strcmp = %ld\n", result);
1053
1054   DestroyWindow(hwndRichEdit);
1055 }
1056
1057 static void test_EM_SETTEXTEX(void)
1058 {
1059   HWND hwndRichEdit = new_richedit(NULL);
1060   SETTEXTEX setText;
1061   GETTEXTEX getText;
1062   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1063                        'S', 'o', 'm', 'e', 
1064                        'T', 'e', 'x', 't', 0}; 
1065   WCHAR TestItem2[] = {'T', 'e', 's', 't',
1066                        'S', 'o', 'm', 'e',
1067                        'T', 'e', 'x', 't',
1068                        '\r', 0};
1069   const char * TestItem2_after = "TestSomeText\r\n";
1070 #define MAX_BUF_LEN 1024
1071   WCHAR buf[MAX_BUF_LEN];
1072   int result;
1073   CHARRANGE cr;
1074
1075   setText.codepage = 1200;  /* no constant for unicode */
1076   getText.codepage = 1200;  /* no constant for unicode */
1077   getText.cb = MAX_BUF_LEN;
1078   getText.flags = GT_DEFAULT;
1079
1080   setText.flags = 0;
1081   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1082   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1083   ok(lstrcmpW(buf, TestItem1) == 0,
1084       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1085
1086   /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1087      convert \r to \r\n on return
1088    */
1089   setText.codepage = 1200;  /* no constant for unicode */
1090   getText.codepage = 1200;  /* no constant for unicode */
1091   getText.cb = MAX_BUF_LEN;
1092   getText.flags = GT_DEFAULT;
1093
1094   setText.flags = 0;
1095   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1096   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1097   ok(lstrcmpW(buf, TestItem2) == 0,
1098       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1099
1100   /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1101   SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1102   ok(strcmp((const char *)buf, TestItem2_after) == 0,
1103       "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1104
1105   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1106                        (WPARAM)&setText, (LPARAM) NULL);
1107   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1108   
1109   ok (result == 1, 
1110       "EM_SETTEXTEX returned %d, instead of 1\n",result);
1111   ok(lstrlenW(buf) == 0,
1112       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1113   
1114   /* put some text back */
1115   setText.flags = 0;
1116   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1117   /* select some text */
1118   cr.cpMax = 1;
1119   cr.cpMin = 3;
1120   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121   /* replace current selection */
1122   setText.flags = ST_SELECTION;
1123   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
1124                        (WPARAM)&setText, (LPARAM) NULL);
1125   ok(result == 0,
1126       "EM_SETTEXTEX with NULL lParam to replace selection"
1127       " with no text should return 0. Got %i\n",
1128       result);
1129   
1130   /* put some text back */
1131   setText.flags = 0;
1132   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1133   /* select some text */
1134   cr.cpMax = 1;
1135   cr.cpMin = 3;
1136   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1137   /* replace current selection */
1138   setText.flags = ST_SELECTION;
1139   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1140                        (WPARAM)&setText, (LPARAM) TestItem1);
1141   /* get text */
1142   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1143   ok(result == lstrlenW(TestItem1),
1144       "EM_SETTEXTEX with NULL lParam to replace selection"
1145       " with no text should return 0. Got %i\n",
1146       result);
1147   ok(lstrlenW(buf) == 22,
1148       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1149       lstrlenW(buf) );
1150
1151   DestroyWindow(hwndRichEdit);
1152 }
1153
1154 static void test_EM_LIMITTEXT(void)
1155 {
1156   int ret;
1157
1158   HWND hwndRichEdit = new_richedit(NULL);
1159
1160   /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1161    * about setting the length to -1 for multiline edit controls doesn't happen.
1162    */
1163
1164   /* Don't check default gettextlimit case. That's done in other tests */
1165
1166   /* Set textlimit to 100 */
1167   SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1168   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1169   ok (ret == 100,
1170       "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1171
1172   /* Set textlimit to 0 */
1173   SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1174   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1175   ok (ret == 65536,
1176       "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1177
1178   /* Set textlimit to -1 */
1179   SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1180   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1181   ok (ret == -1,
1182       "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1183
1184   /* Set textlimit to -2 */
1185   SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1186   ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1187   ok (ret == -2,
1188       "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1189
1190   DestroyWindow (hwndRichEdit);
1191 }
1192
1193
1194 static void test_EM_EXLIMITTEXT(void)
1195 {
1196   int i, selBegin, selEnd, len1, len2;
1197   int result;
1198   char text[1024 + 1];
1199   char buffer[1024 + 1];
1200   int textlimit = 0; /* multiple of 100 */
1201   HWND hwndRichEdit = new_richedit(NULL);
1202   
1203   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1204   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1205   
1206   textlimit = 256000;
1207   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1208   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1209   /* set higher */
1210   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1211   
1212   textlimit = 1000;
1213   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1214   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1215   /* set lower */
1216   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1217  
1218   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1219   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1220   /* default for WParam = 0 */
1221   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1222  
1223   textlimit = sizeof(text)-1;
1224   memset(text, 'W', textlimit);
1225   text[sizeof(text)-1] = 0;
1226   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1227   /* maxed out text */
1228   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1229   
1230   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1231   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1232   len1 = selEnd - selBegin;
1233   
1234   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1235   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1236   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1237   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1238   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1239   len2 = selEnd - selBegin;
1240   
1241   ok(len1 != len2,
1242     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1243     len1,len2,i);
1244   
1245   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1246   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1247   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1248   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1249   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1250   len1 = selEnd - selBegin;
1251   
1252   ok(len1 != len2,
1253     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1254     len1,len2,i);
1255   
1256   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1257   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1258   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1259   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1260   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1261   len2 = selEnd - selBegin;
1262   
1263   ok(len1 == len2, 
1264     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1265     len1,len2,i);
1266
1267   /* set text up to the limit, select all the text, then add a char */
1268   textlimit = 5;
1269   memset(text, 'W', textlimit);
1270   text[textlimit] = 0;
1271   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1272   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1273   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1274   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1275   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1276   result = strcmp(buffer, "A");
1277   ok(0 == result, "got string = \"%s\"\n", buffer);
1278
1279   /* WM_SETTEXT not limited */
1280   textlimit = 10;
1281   memset(text, 'W', textlimit);
1282   text[textlimit] = 0;
1283   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1284   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1285   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1286   i = strlen(buffer);
1287   ok(10 == i, "expected 10 chars\n");
1288   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1289   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1290
1291   /* try inserting more text at end */
1292   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1293   ok(0 == i, "WM_CHAR wasn't processed\n");
1294   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1295   i = strlen(buffer);
1296   ok(10 == i, "expected 10 chars, got %i\n", i);
1297   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1298   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1299
1300   /* try inserting text at beginning */
1301   SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1302   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1303   ok(0 == i, "WM_CHAR wasn't processed\n");
1304   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1305   i = strlen(buffer);
1306   ok(10 == i, "expected 10 chars, got %i\n", i);
1307   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1308   ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1309
1310   /* WM_CHAR is limited */
1311   textlimit = 1;
1312   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1313   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1314   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1315   ok(0 == i, "WM_CHAR wasn't processed\n");
1316   i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1317   ok(0 == i, "WM_CHAR wasn't processed\n");
1318   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1319   i = strlen(buffer);
1320   ok(1 == i, "expected 1 chars, got %i instead\n", i);
1321
1322   DestroyWindow(hwndRichEdit);
1323 }
1324
1325 static void test_EM_GETLIMITTEXT(void)
1326 {
1327   int i;
1328   HWND hwndRichEdit = new_richedit(NULL);
1329
1330   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1331   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1332
1333   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1334   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1335   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1336
1337   DestroyWindow(hwndRichEdit);
1338 }
1339
1340 static void test_WM_SETFONT(void)
1341 {
1342   /* There is no invalid input or error conditions for this function.
1343    * NULL wParam and lParam just fall back to their default values 
1344    * It should be noted that even if you use a gibberish name for your fonts
1345    * here, it will still work because the name is stored. They will display as
1346    * System, but will report their name to be whatever they were created as */
1347   
1348   HWND hwndRichEdit = new_richedit(NULL);
1349   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1350     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1351     FF_DONTCARE, "Marlett");
1352   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1353     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1354     FF_DONTCARE, "MS Sans Serif");
1355   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1356     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1357     FF_DONTCARE, "Courier");
1358   LOGFONTA sentLogFont;
1359   CHARFORMAT2A returnedCF2A;
1360   
1361   returnedCF2A.cbSize = sizeof(returnedCF2A);
1362   
1363   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1364   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1365   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1366
1367   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1368   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1369     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1370     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1371
1372   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1373   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1374   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1375   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1376     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1377     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1378     
1379   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1380   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1381   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1382   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1383     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1384     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1385    
1386   /* This last test is special since we send in NULL. We clear the variables
1387    * and just compare to "System" instead of the sent in font name. */
1388   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1389   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1390   returnedCF2A.cbSize = sizeof(returnedCF2A);
1391   
1392   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1393   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1394   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1395   ok (!strcmp("System",returnedCF2A.szFaceName),
1396     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1397   
1398   DestroyWindow(hwndRichEdit);
1399 }
1400
1401
1402 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1403                                          LPBYTE pbBuff,
1404                                          LONG cb,
1405                                          LONG *pcb)
1406 {
1407   const char** str = (const char**)dwCookie;
1408   int size = strlen(*str);
1409   if(size > 3)  /* let's make it peice-meal for fun */
1410     size = 3;
1411   *pcb = cb;
1412   if (*pcb > size) {
1413     *pcb = size;
1414   }
1415   if (*pcb > 0) {
1416     memcpy(pbBuff, *str, *pcb);
1417     *str += *pcb;
1418   }
1419   return 0;
1420 }
1421
1422 static void test_EM_GETMODIFY(void)
1423 {
1424   HWND hwndRichEdit = new_richedit(NULL);
1425   LRESULT result;
1426   SETTEXTEX setText;
1427   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1428                        'S', 'o', 'm', 'e', 
1429                        'T', 'e', 'x', 't', 0}; 
1430   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1431                        'S', 'o', 'm', 'e', 
1432                        'O', 't', 'h', 'e', 'r',
1433                        'T', 'e', 'x', 't', 0}; 
1434   const char* streamText = "hello world";
1435   CHARFORMAT2 cf2;
1436   PARAFORMAT2 pf2;
1437   EDITSTREAM es;
1438   
1439   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1440     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1441     FF_DONTCARE, "Courier");
1442   
1443   setText.codepage = 1200;  /* no constant for unicode */
1444   setText.flags = ST_KEEPUNDO;
1445   
1446
1447   /* modify flag shouldn't be set when richedit is first created */
1448   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1449   ok (result == 0, 
1450       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1451   
1452   /* setting modify flag should actually set it */
1453   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1454   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1455   ok (result != 0, 
1456       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1457   
1458   /* clearing modify flag should actually clear it */
1459   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1460   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1461   ok (result == 0, 
1462       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1463  
1464   /* setting font doesn't change modify flag */
1465   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1466   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1467   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1468   ok (result == 0,
1469       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1470
1471   /* setting text should set modify flag */
1472   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1473   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1474   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1475   ok (result != 0,
1476       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1477   
1478   /* undo previous text doesn't reset modify flag */
1479   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1480   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1481   ok (result != 0,
1482       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1483   
1484   /* set text with no flag to keep undo stack should not set modify flag */
1485   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1486   setText.flags = 0;
1487   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1488   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1489   ok (result == 0,
1490       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1491   
1492   /* WM_SETTEXT doesn't modify */
1493   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1494   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1495   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1496   todo_wine {
1497   ok (result == 0,
1498       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1499   }
1500   
1501   /* clear the text */
1502   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1503   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1504   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1505   ok (result == 0,
1506       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1507   
1508   /* replace text */
1509   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1510   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1511   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1512   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1513   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1514   ok (result != 0,
1515       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1516   
1517   /* copy/paste text 1 */
1518   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1519   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1520   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1521   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1522   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1523   ok (result != 0,
1524       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1525   
1526   /* copy/paste text 2 */
1527   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1528   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1529   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1530   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1531   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1532   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1533   ok (result != 0,
1534       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1535   
1536   /* press char */
1537   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1538   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1539   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1540   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1541   ok (result != 0,
1542       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1543
1544   /* press del */
1545   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1546   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1547   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1548   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1549   ok (result != 0,
1550       "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1551   
1552   /* set char format */
1553   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1554   cf2.cbSize = sizeof(CHARFORMAT2);
1555   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1556              (LPARAM) &cf2);
1557   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1558   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1559   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1560   result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1561   ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1562   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1563   ok (result != 0,
1564       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1565   
1566   /* set para format */
1567   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1568   pf2.cbSize = sizeof(PARAFORMAT2);
1569   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1570              (LPARAM) &pf2);
1571   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1572   pf2.wAlignment = PFA_RIGHT;
1573   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1574   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1575   ok (result == 0,
1576       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1577
1578   /* EM_STREAM */
1579   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1580   es.dwCookie = (DWORD_PTR)&streamText;
1581   es.dwError = 0;
1582   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1583   SendMessage(hwndRichEdit, EM_STREAMIN, 
1584               (WPARAM)(SF_TEXT), (LPARAM)&es);
1585   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1586   ok (result != 0,
1587       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1588
1589   DestroyWindow(hwndRichEdit);
1590 }
1591
1592 struct exsetsel_s {
1593   long min;
1594   long max;
1595   long expected_retval;
1596   int expected_getsel_start;
1597   int expected_getsel_end;
1598   int _exsetsel_todo_wine;
1599   int _getsel_todo_wine;
1600 };
1601
1602 const struct exsetsel_s exsetsel_tests[] = {
1603   /* sanity tests */
1604   {5, 10, 10, 5, 10, 0, 0},
1605   {15, 17, 17, 15, 17, 0, 0},
1606   /* test cpMax > strlen() */
1607   {0, 100, 18, 0, 18, 0, 1},
1608   /* test cpMin == cpMax */
1609   {5, 5, 5, 5, 5, 0, 0},
1610   /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1611   {-1, 0, 5, 5, 5, 0, 0},
1612   {-1, 17, 5, 5, 5, 0, 0},
1613   {-1, 18, 5, 5, 5, 0, 0},
1614   /* test cpMin < 0 && cpMax < 0 */
1615   {-1, -1, 17, 17, 17, 0, 0},
1616   {-4, -5, 17, 17, 17, 0, 0},
1617   /* test cMin >=0 && cpMax < 0 (bug 6814) */
1618   {0, -1, 18, 0, 18, 0, 1},
1619   {17, -5, 18, 17, 18, 0, 1},
1620   {18, -3, 17, 17, 17, 0, 0},
1621   /* test if cpMin > cpMax */
1622   {15, 19, 18, 15, 18, 0, 1},
1623   {19, 15, 18, 15, 18, 0, 1}
1624 };
1625
1626 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1627     CHARRANGE cr;
1628     long result;
1629     int start, end;
1630
1631     cr.cpMin = setsel->min;
1632     cr.cpMax = setsel->max;
1633     result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1634
1635     if (setsel->_exsetsel_todo_wine) {
1636         todo_wine {
1637             ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1638         }
1639     } else {
1640         ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1641     }
1642
1643     SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1644
1645     if (setsel->_getsel_todo_wine) {
1646         todo_wine {
1647             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);
1648         }
1649     } else {
1650         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);
1651     }
1652 }
1653
1654 static void test_EM_EXSETSEL(void)
1655 {
1656     HWND hwndRichEdit = new_richedit(NULL);
1657     int i;
1658     const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1659
1660     /* sending some text to the window */
1661     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1662     /*                                                 01234567890123456*/
1663     /*                                                          10      */
1664
1665     for (i = 0; i < num_tests; i++) {
1666         check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1667     }
1668
1669     DestroyWindow(hwndRichEdit);
1670 }
1671
1672 static void test_EM_REPLACESEL(void)
1673 {
1674     HWND hwndRichEdit = new_richedit(NULL);
1675     char buffer[1024] = {0};
1676     int r;
1677
1678     /* sending some text to the window */
1679     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1680     /*                                                 01234567890123456*/
1681     /*                                                          10      */
1682
1683     /* FIXME add more tests */
1684     SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1685     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1686     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1687     r = strcmp(buffer, "testing");
1688     ok(0 == r, "expected %d, got %d\n", 0, r);
1689
1690     DestroyWindow(hwndRichEdit);
1691 }
1692
1693 static void test_WM_PASTE(void)
1694 {
1695     int result;
1696     char buffer[1024] = {0};
1697     const char* text1 = "testing paste\r";
1698     const char* text1_after = "testing paste\r\n";
1699     const char* text2 = "testing paste\r\rtesting paste";
1700     const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1701     HWND hwndRichEdit = new_richedit(NULL);
1702
1703     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1704     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1705     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1706     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1707     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1708     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1709     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1710     result = strcmp(text1_after, buffer);
1711     ok(result == 0,
1712         "test paste: strcmp = %i\n", result);
1713
1714     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1715     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1716     SendMessage(hwndRichEdit, WM_CHAR, 3, 0);  /* ctrl-c */
1717     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1718     SendMessage(hwndRichEdit, WM_CHAR, 22, 0);  /* ctrl-v */
1719     SendMessage(hwndRichEdit, WM_CHAR, 26, 0);  /* ctrl-z */
1720     SendMessage(hwndRichEdit, WM_CHAR, 25, 0);  /* ctrl-y */
1721     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1722     result = strcmp(buffer,text3);
1723     ok(result == 0,
1724         "test paste: strcmp = %i\n", result);
1725
1726     DestroyWindow(hwndRichEdit);
1727 }
1728
1729 static void test_EM_FORMATRANGE(void)
1730 {
1731   int r;
1732   FORMATRANGE fr;
1733   HDC hdc;
1734   HWND hwndRichEdit = new_richedit(NULL);
1735
1736   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1737
1738   hdc = GetDC(hwndRichEdit);
1739   ok(hdc != NULL, "Could not get HDC\n");
1740
1741   fr.hdc = fr.hdcTarget = hdc;
1742   fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1743   fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1744   fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1745   fr.chrg.cpMin = 0;
1746   fr.chrg.cpMax = 20;
1747
1748   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1749   todo_wine {
1750     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1751   }
1752
1753   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1754   todo_wine {
1755     ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1756   }
1757
1758   fr.chrg.cpMin = 0;
1759   fr.chrg.cpMax = 10;
1760
1761   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1762   todo_wine {
1763     ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1764   }
1765
1766   r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1767   todo_wine {
1768     ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1769   }
1770
1771   DestroyWindow(hwndRichEdit);
1772 }
1773
1774 static int nCallbackCount = 0;
1775
1776 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1777                                  LONG cb, LONG* pcb)
1778 {
1779   const char text[] = {'t','e','s','t'};
1780
1781   if (sizeof(text) <= cb)
1782   {
1783     if ((int)dwCookie != nCallbackCount)
1784     {
1785       *pcb = 0;
1786       return 0;
1787     }
1788
1789     memcpy (pbBuff, text, sizeof(text));
1790     *pcb = sizeof(text);
1791
1792     nCallbackCount++;
1793
1794     return 0;
1795   }
1796   else
1797     return 1; /* indicates callback failed */
1798 }
1799
1800 static void test_EM_StreamIn_Undo(void)
1801 {
1802   /* The purpose of this test is to determine when a EM_StreamIn should be
1803    * undoable. This is important because WM_PASTE currently uses StreamIn and
1804    * pasting should always be undoable but streaming isn't always.
1805    *
1806    * cases to test:
1807    * StreamIn plain text without SFF_SELECTION.
1808    * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1809    * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1810    * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1811    * Feel free to add tests for other text modes or StreamIn things.
1812    */
1813
1814
1815   HWND hwndRichEdit = new_richedit(NULL);
1816   LRESULT result;
1817   EDITSTREAM es;
1818   char buffer[1024] = {0};
1819   const char randomtext[] = "Some text";
1820
1821   es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1822
1823   /* StreamIn, no SFF_SELECTION */
1824   es.dwCookie = nCallbackCount;
1825   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1826   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1827   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1828   SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1829   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1830   result = strcmp (buffer,"test");
1831   ok (result  == 0,
1832       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1833
1834   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1835   ok (result == FALSE,
1836       "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1837
1838   /* StreamIn, SFF_SELECTION, but nothing selected */
1839   es.dwCookie = nCallbackCount;
1840   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1841   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1842   SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1843   SendMessage(hwndRichEdit, EM_STREAMIN,
1844               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1845   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1846   result = strcmp (buffer,"testSome text");
1847   ok (result  == 0,
1848       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1849
1850   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1851   ok (result == TRUE,
1852      "EM_STREAMIN with SFF_SELECTION but no selection set "
1853       "should create an undo\n");
1854
1855   /* StreamIn, SFF_SELECTION, with a selection */
1856   es.dwCookie = nCallbackCount;
1857   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1858   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1859   SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1860   SendMessage(hwndRichEdit, EM_STREAMIN,
1861               (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1862   SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1863   result = strcmp (buffer,"Sometesttext");
1864   ok (result  == 0,
1865       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1866
1867   result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1868   ok (result == TRUE,
1869       "EM_STREAMIN with SFF_SELECTION and selection set "
1870       "should create an undo\n");
1871
1872 }
1873
1874 static BOOL is_em_settextex_supported(HWND hwnd)
1875 {
1876     SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1877     return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1878 }
1879
1880 static void test_unicode_conversions(void)
1881 {
1882     static const WCHAR tW[] = {'t',0};
1883     static const WCHAR teW[] = {'t','e',0};
1884     static const WCHAR textW[] = {'t','e','s','t',0};
1885     static const char textA[] = "test";
1886     char bufA[64];
1887     WCHAR bufW[64];
1888     HWND hwnd;
1889     int is_win9x, em_settextex_supported, ret;
1890
1891     is_win9x = GetVersion() & 0x80000000;
1892
1893 #define set_textA(hwnd, wm_set_text, txt) \
1894     do { \
1895         SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1896         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1897         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1898         ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1899         ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1900     } while(0)
1901 #define expect_textA(hwnd, wm_get_text, txt) \
1902     do { \
1903         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1904         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1905         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1906         memset(bufA, 0xAA, sizeof(bufA)); \
1907         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1908         ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1909         ret = lstrcmpA(bufA, txt); \
1910         ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1911     } while(0)
1912
1913 #define set_textW(hwnd, wm_set_text, txt) \
1914     do { \
1915         SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1916         WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1917         assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1918         ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1919         ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1920     } while(0)
1921 #define expect_textW(hwnd, wm_get_text, txt) \
1922     do { \
1923         GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1924         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1925         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1926         memset(bufW, 0xAA, sizeof(bufW)); \
1927         if (is_win9x) \
1928         { \
1929             assert(wm_get_text == EM_GETTEXTEX); \
1930             ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1931             ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1932         } \
1933         else \
1934         { \
1935             ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1936             ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1937         } \
1938         ret = lstrcmpW(bufW, txt); \
1939         ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1940     } while(0)
1941 #define expect_empty(hwnd, wm_get_text) \
1942     do { \
1943         GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1944         WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1945         assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1946         memset(bufA, 0xAA, sizeof(bufA)); \
1947         ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1948         ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1949         ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1950     } while(0)
1951
1952     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1953                            0, 0, 200, 60, 0, 0, 0, 0);
1954     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1955
1956     ret = IsWindowUnicode(hwnd);
1957     if (is_win9x)
1958         ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1959     else
1960         ok(ret, "RichEdit20W should be unicode under NT\n");
1961
1962     /* EM_SETTEXTEX is supported starting from version 3.0 */
1963     em_settextex_supported = is_em_settextex_supported(hwnd);
1964     trace("EM_SETTEXTEX is %ssupported on this platform\n",
1965           em_settextex_supported ? "" : "NOT ");
1966
1967     expect_empty(hwnd, WM_GETTEXT);
1968     expect_empty(hwnd, EM_GETTEXTEX);
1969
1970     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1971     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1972     expect_textA(hwnd, WM_GETTEXT, "t");
1973     expect_textA(hwnd, EM_GETTEXTEX, "t");
1974     expect_textW(hwnd, EM_GETTEXTEX, tW);
1975
1976     ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1977     ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1978     expect_textA(hwnd, WM_GETTEXT, "te");
1979     expect_textA(hwnd, EM_GETTEXTEX, "te");
1980     expect_textW(hwnd, EM_GETTEXTEX, teW);
1981
1982     set_textA(hwnd, WM_SETTEXT, NULL);
1983     expect_empty(hwnd, WM_GETTEXT);
1984     expect_empty(hwnd, EM_GETTEXTEX);
1985
1986     if (is_win9x)
1987         set_textA(hwnd, WM_SETTEXT, textW);
1988     else
1989         set_textA(hwnd, WM_SETTEXT, textA);
1990     expect_textA(hwnd, WM_GETTEXT, textA);
1991     expect_textA(hwnd, EM_GETTEXTEX, textA);
1992     expect_textW(hwnd, EM_GETTEXTEX, textW);
1993
1994     if (em_settextex_supported)
1995     {
1996         set_textA(hwnd, EM_SETTEXTEX, textA);
1997         expect_textA(hwnd, WM_GETTEXT, textA);
1998         expect_textA(hwnd, EM_GETTEXTEX, textA);
1999         expect_textW(hwnd, EM_GETTEXTEX, textW);
2000     }
2001
2002     if (!is_win9x)
2003     {
2004         set_textW(hwnd, WM_SETTEXT, textW);
2005         expect_textW(hwnd, WM_GETTEXT, textW);
2006         expect_textA(hwnd, WM_GETTEXT, textA);
2007         expect_textW(hwnd, EM_GETTEXTEX, textW);
2008         expect_textA(hwnd, EM_GETTEXTEX, textA);
2009
2010         if (em_settextex_supported)
2011         {
2012             set_textW(hwnd, EM_SETTEXTEX, textW);
2013             expect_textW(hwnd, WM_GETTEXT, textW);
2014             expect_textA(hwnd, WM_GETTEXT, textA);
2015             expect_textW(hwnd, EM_GETTEXTEX, textW);
2016             expect_textA(hwnd, EM_GETTEXTEX, textA);
2017         }
2018     }
2019     DestroyWindow(hwnd);
2020
2021     hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2022                            0, 0, 200, 60, 0, 0, 0, 0);
2023     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2024
2025     ret = IsWindowUnicode(hwnd);
2026     ok(!ret, "RichEdit20A should NOT be unicode\n");
2027
2028     set_textA(hwnd, WM_SETTEXT, textA);
2029     expect_textA(hwnd, WM_GETTEXT, textA);
2030     expect_textA(hwnd, EM_GETTEXTEX, textA);
2031     expect_textW(hwnd, EM_GETTEXTEX, textW);
2032
2033     if (em_settextex_supported)
2034     {
2035         set_textA(hwnd, EM_SETTEXTEX, textA);
2036         expect_textA(hwnd, WM_GETTEXT, textA);
2037         expect_textA(hwnd, EM_GETTEXTEX, textA);
2038         expect_textW(hwnd, EM_GETTEXTEX, textW);
2039     }
2040
2041     if (!is_win9x)
2042     {
2043         set_textW(hwnd, WM_SETTEXT, textW);
2044         expect_textW(hwnd, WM_GETTEXT, textW);
2045         expect_textA(hwnd, WM_GETTEXT, textA);
2046         expect_textW(hwnd, EM_GETTEXTEX, textW);
2047         expect_textA(hwnd, EM_GETTEXTEX, textA);
2048
2049         if (em_settextex_supported)
2050         {
2051             set_textW(hwnd, EM_SETTEXTEX, textW);
2052             expect_textW(hwnd, WM_GETTEXT, textW);
2053             expect_textA(hwnd, WM_GETTEXT, textA);
2054             expect_textW(hwnd, EM_GETTEXTEX, textW);
2055             expect_textA(hwnd, EM_GETTEXTEX, textA);
2056         }
2057     }
2058     DestroyWindow(hwnd);
2059 }
2060
2061
2062 static void test_EM_GETTEXTLENGTHEX(void)
2063 {
2064     HWND hwnd;
2065     GETTEXTLENGTHEX gtl;
2066     int ret;
2067
2068     /* single line */
2069     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2070                            0, 0, 200, 60, 0, 0, 0, 0);
2071     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2072
2073     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2074     gtl.codepage = CP_ACP;
2075     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2076     ok(ret == 0, "ret %d\n",ret);
2077
2078     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2079     gtl.codepage = CP_ACP;
2080     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2081     ok(ret == 0, "ret %d\n",ret);
2082
2083     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
2084
2085     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2086     gtl.codepage = CP_ACP;
2087     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2088     todo_wine ok(ret == 1, "ret %d\n",ret);
2089
2090     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2091     gtl.codepage = CP_ACP;
2092     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2093     todo_wine ok(ret == 1, "ret %d\n",ret);
2094
2095     DestroyWindow(hwnd);
2096
2097     /* multi line */
2098     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2099                            0, 0, 200, 60, 0, 0, 0, 0);
2100     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2101
2102     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2103     gtl.codepage = CP_ACP;
2104     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2105     todo_wine ok(ret == 0, "ret %d\n",ret);
2106
2107     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2108     gtl.codepage = CP_ACP;
2109     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2110     ok(ret == 0, "ret %d\n",ret);
2111
2112     SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
2113
2114     gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2115     gtl.codepage = CP_ACP;
2116     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2117     todo_wine ok(ret == 10, "ret %d\n",ret);
2118
2119     gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2120     gtl.codepage = CP_ACP;
2121     ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2122     ok(ret == 6, "ret %d\n",ret);
2123
2124     DestroyWindow(hwnd);
2125 }
2126
2127
2128 /* globals that parent and child access when checking event masks & notifications */
2129 static HWND eventMaskEditHwnd = 0;
2130 static int queriedEventMask;
2131 static int watchForEventMask = 0;
2132
2133 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2134 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2135 {
2136     if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2137     {
2138       queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2139     }
2140     return DefWindowProcA(hwnd, message, wParam, lParam);
2141 }
2142
2143 /* test event masks in combination with WM_COMMAND */
2144 static void test_eventMask(void)
2145 {
2146     HWND parent;
2147     int ret;
2148     WNDCLASSA cls;
2149     const char text[] = "foo bar\n";
2150     int eventMask;
2151
2152     /* register class to capture WM_COMMAND */
2153     cls.style = 0;
2154     cls.lpfnWndProc = ParentMsgCheckProcA;
2155     cls.cbClsExtra = 0;
2156     cls.cbWndExtra = 0;
2157     cls.hInstance = GetModuleHandleA(0);
2158     cls.hIcon = 0;
2159     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2160     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2161     cls.lpszMenuName = NULL;
2162     cls.lpszClassName = "EventMaskParentClass";
2163     if(!RegisterClassA(&cls)) assert(0);
2164
2165     parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2166                           0, 0, 200, 60, NULL, NULL, NULL, NULL);
2167     ok (parent != 0, "Failed to create parent window\n");
2168
2169     eventMaskEditHwnd = new_richedit(parent);
2170     ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2171
2172     eventMask = ENM_CHANGE | ENM_UPDATE;
2173     ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2174     ok(ret == ENM_NONE, "wrong event mask\n");
2175     ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2176     ok(ret == eventMask, "failed to set event mask\n");
2177
2178     /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2179     queriedEventMask = 0;  /* initialize to something other than we expect */
2180     watchForEventMask = EN_CHANGE;
2181     ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2182     ok(ret == TRUE, "failed to set text\n");
2183     /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2184        notification in response to WM_SETTEXT */
2185     ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2186             "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2187
2188 }
2189
2190
2191 START_TEST( editor )
2192 {
2193   MSG msg;
2194   time_t end;
2195
2196   /* Must explicitly LoadLibrary(). The test has no references to functions in
2197    * RICHED20.DLL, so the linker doesn't actually link to it. */
2198   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2199   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2200
2201   test_EM_FINDTEXT();
2202   test_EM_GETLINE();
2203   test_EM_SCROLLCARET();
2204   test_EM_SCROLL();
2205   test_WM_SETTEXT();
2206   test_EM_SETCHARFORMAT();
2207   test_EM_SETTEXTMODE();
2208   test_TM_PLAINTEXT();
2209   test_EM_SETOPTIONS();
2210   test_WM_GETTEXT();
2211   test_EM_AUTOURLDETECT();
2212   test_EM_SETUNDOLIMIT();
2213   test_ES_PASSWORD();
2214   test_EM_SETTEXTEX();
2215   test_EM_LIMITTEXT();
2216   test_EM_EXLIMITTEXT();
2217   test_EM_GETLIMITTEXT();
2218   test_WM_SETFONT();
2219   test_EM_GETMODIFY();
2220   test_EM_EXSETSEL();
2221   test_WM_PASTE();
2222   test_EM_StreamIn_Undo();
2223   test_EM_FORMATRANGE();
2224   test_unicode_conversions();
2225   test_EM_GETTEXTLENGTHEX();
2226   test_EM_REPLACESEL();
2227   test_eventMask();
2228
2229   /* Set the environment variable WINETEST_RICHED20 to keep windows
2230    * responsive and open for 30 seconds. This is useful for debugging.
2231    *
2232    * The message pump uses PeekMessage() to empty the queue and then sleeps for
2233    * 50ms before retrying the queue. */
2234   end = time(NULL) + 30;
2235   if (getenv( "WINETEST_RICHED20" )) {
2236     while (time(NULL) < end) {
2237       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2238         TranslateMessage(&msg);
2239         DispatchMessage(&msg);
2240       } else {
2241         Sleep(50);
2242       }
2243     }
2244   }
2245
2246   OleFlushClipboard();
2247   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
2248 }